// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Api computes the exported API of a set of Go packages.

//modify 2013-2014 visualfc

package goapi

import (
	"bufio"
	"bytes"
	"fmt"
	"go/ast"
	"go/build"
	"go/doc"
	"go/parser"
	"go/printer"
	"go/token"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/visualfc/gotools/pkg/command"
)

var Command = &command.Command{
	Run:       runApi,
	UsageLine: "goapi",
	Short:     "golang api util",
	Long:      `golang api util`,
}

var apiVerbose bool
var apiAllmethods bool
var apiAlldecls bool
var apiShowpos bool
var apiSeparate string
var apiImportParser bool
var apiDefaultCtx bool
var apiCustomCtx string
var apiLookupInfo string
var apiLookupStdin bool
var apiOutput string

func init() {
	Command.Flag.BoolVar(&apiVerbose, "v", false, "verbose debugging")
	Command.Flag.BoolVar(&apiAllmethods, "e", true, "extract for all embedded methods")
	Command.Flag.BoolVar(&apiAlldecls, "a", false, "extract for all declarations")
	Command.Flag.BoolVar(&apiShowpos, "pos", false, "addition token position")
	Command.Flag.StringVar(&apiSeparate, "sep", ", ", "setup separators")
	Command.Flag.BoolVar(&apiImportParser, "dep", true, "parser package imports")
	Command.Flag.BoolVar(&apiDefaultCtx, "default_ctx", true, "extract for default context")
	Command.Flag.StringVar(&apiCustomCtx, "custom_ctx", "", "optional comma-separated list of <goos>-<goarch>[-cgo] to override default contexts.")
	Command.Flag.StringVar(&apiLookupInfo, "cursor_info", "", "lookup cursor node info\"file.go:pos\"")
	Command.Flag.BoolVar(&apiLookupStdin, "cursor_std", false, "cursor_info use stdin")
	Command.Flag.StringVar(&apiOutput, "o", "", "output file")
}

func runApi(cmd *command.Command, args []string) error {
	if len(args) == 0 && apiLookupInfo == "" {
		cmd.Usage()
		return os.ErrInvalid
	}
	if apiVerbose {
		now := time.Now()
		defer func() {
			log.Println("time", time.Now().Sub(now))
		}()
	}

	var pkgs []string
	if len(args) > 0 {
		if args[0] == "std" || args[0] == "all" {
			out, err := exec.Command("go", "list", "-e", args[0]).Output()
			if err != nil {
				log.Fatal(err)
			}
			pkgs = strings.Fields(string(out))
		} else {
			pkgs = args
		}
	}
	var curinfo CursorInfo
	if apiLookupInfo != "" {
		pos := strings.Index(apiLookupInfo, ":")
		if pos != -1 {
			curinfo.file = (apiLookupInfo)[:pos]
			if i, err := strconv.Atoi((apiLookupInfo)[pos+1:]); err == nil {
				curinfo.pos = token.Pos(i)
			}
		}
	}

	if len(pkgs) == 1 && curinfo.pos != token.NoPos {
		curinfo.pkg = pkgs[0]
	}

	if apiLookupStdin {
		src, err := ioutil.ReadAll(os.Stdin)
		if err == nil {
			curinfo.src = src
			curinfo.std = true
		}
	}

	if apiCustomCtx != "" {
		apiDefaultCtx = false
		setCustomContexts()
	}

	var features []string
	w := NewWalker()
	if curinfo.pkg != "" {
		w.cursorInfo = &curinfo
	}
	w.sep = apiSeparate

	if apiDefaultCtx {
		w.context = &build.Default

		for _, pkg := range pkgs {
			w.wantedPkg[pkg] = true
		}

		for _, pkg := range pkgs {
			w.WalkPackage(pkg)
		}
		if w.cursorInfo != nil {
			goto lookup
		} else {
			var file io.Writer
			if apiOutput != "" {
				var err error
				file, err = os.Create(apiOutput)
				if err != nil {
					log.Fatal(err)
				}
			} else {
				file = os.Stdout
			}
			bw := bufio.NewWriter(file)
			defer bw.Flush()
			for _, p := range w.packageMap {
				if w.wantedPkg[p.name] {
					for _, f := range p.Features() {
						fmt.Fprintf(bw, "%s\n", f)
					}
				}
			}
			return nil
		}
		features = w.Features("")
	} else {
		for _, c := range contexts {
			c.Compiler = build.Default.Compiler
		}

		for _, pkg := range pkgs {
			w.wantedPkg[pkg] = true
		}

		var featureCtx = make(map[string]map[string]bool) // feature -> context name -> true
		for _, context := range contexts {
			w.context = context
			w.ctxName = contextName(w.context) + ":"

			for _, pkg := range pkgs {
				w.WalkPackage(pkg)
			}
			if w.cursorInfo != nil && w.cursorInfo.info != nil {
				goto lookup
			}
		}

		for pkg, p := range w.packageMap {
			if w.wantedPkg[p.name] {
				pos := strings.Index(pkg, ":")
				if pos == -1 {
					continue
				}
				ctxName := pkg[:pos]
				for _, f := range p.Features() {
					if featureCtx[f] == nil {
						featureCtx[f] = make(map[string]bool)
					}
					featureCtx[f][ctxName] = true
				}
			}
		}

		for f, cmap := range featureCtx {
			if len(cmap) == len(contexts) {
				features = append(features, f)
				continue
			}
			comma := strings.Index(f, ",")
			for cname := range cmap {
				f2 := fmt.Sprintf("%s (%s)%s", f[:comma], cname, f[comma:])
				features = append(features, f2)
			}
		}
		sort.Strings(features)
	}

lookup:
	if w.cursorInfo != nil {
		info := w.cursorInfo.info
		if info == nil {
			os.Exit(1)
			return os.ErrInvalid
		}
		//		fmt.Println("kind,", info.Kind)
		//		fmt.Println("name,", info.Name)
		//		if info.Type != "" {
		//			fmt.Println("type,", strings.TrimLeft(info.Type, "*"))
		//		}
		if info.Name == info.Type || info.Type == "" {
			fmt.Printf("info, %s, %s\n", info.Kind, info.Name)
		} else {
			fmt.Printf("info, %s, %s, %s\n", info.Kind, info.Name, info.Type)
		}
		if info.Kind == KindImport || info.Kind == KindPackage {
			if p := w.findPackage(info.Name); p != nil {
				fmt.Println("help,", p.name)
			}
		}
		if info.T != nil {
			for _, text := range []string{info.Name, info.Type} {
				typ := strings.TrimLeft(text, "*")
				pos := strings.Index(typ, ".")
				if pos != -1 {
					if p := w.findPackage(typ[:pos]); p != nil {
						fmt.Println("help,", p.name+typ[pos:])
						break
					}
				}
			}
			fmt.Println("pos,", w.fset.Position(info.T.Pos()))
		}
		return nil
	}

	fail := false
	defer func() {
		if fail {
			os.Exit(1)
		}
	}()

	bw := bufio.NewWriter(os.Stdout)
	defer bw.Flush()

	for _, f := range features {
		fmt.Fprintf(bw, "%s\n", f)
	}
	return nil
}

type CursorInfo struct {
	pkg  string
	file string
	pos  token.Pos
	src  []byte
	std  bool
	info *TypeInfo
}

// contexts are the default contexts which are scanned, unless
// overridden by the -contexts flag.
var contexts = []*build.Context{
	{GOOS: "linux", GOARCH: "386", CgoEnabled: true},
	{GOOS: "linux", GOARCH: "386"},
	{GOOS: "linux", GOARCH: "amd64", CgoEnabled: true},
	{GOOS: "linux", GOARCH: "amd64"},
	{GOOS: "linux", GOARCH: "arm"},
	{GOOS: "darwin", GOARCH: "386", CgoEnabled: true},
	{GOOS: "darwin", GOARCH: "386"},
	{GOOS: "darwin", GOARCH: "amd64", CgoEnabled: true},
	{GOOS: "darwin", GOARCH: "amd64"},
	{GOOS: "windows", GOARCH: "amd64"},
	{GOOS: "windows", GOARCH: "386"},
	{GOOS: "freebsd", GOARCH: "amd64"},
	{GOOS: "freebsd", GOARCH: "386"},
}

func contextName(c *build.Context) string {
	s := c.GOOS + "-" + c.GOARCH
	if c.CgoEnabled {
		return s + "-cgo"
	}
	return s
}

func osArchName(c *build.Context) string {
	return c.GOOS + "-" + c.GOARCH
}

func parseContext(c string) *build.Context {
	parts := strings.Split(c, "-")
	if len(parts) < 2 {
		log.Fatalf("bad context: %q", c)
	}
	bc := &build.Context{
		GOOS:   parts[0],
		GOARCH: parts[1],
	}
	if len(parts) == 3 {
		if parts[2] == "cgo" {
			bc.CgoEnabled = true
		} else {
			log.Fatalf("bad context: %q", c)
		}
	}
	return bc
}

func setCustomContexts() {
	contexts = []*build.Context{}
	for _, c := range strings.Split(apiCustomCtx, ",") {
		contexts = append(contexts, parseContext(c))
	}
}

func set(items []string) map[string]bool {
	s := make(map[string]bool)
	for _, v := range items {
		s[v] = true
	}
	return s
}

var spaceParensRx = regexp.MustCompile(` \(\S+?\)`)

func featureWithoutContext(f string) string {
	if !strings.Contains(f, "(") {
		return f
	}
	return spaceParensRx.ReplaceAllString(f, "")
}

func compareAPI(w io.Writer, features, required, optional, exception []string, allowNew bool) (ok bool) {
	ok = true

	optionalSet := set(optional)
	exceptionSet := set(exception)
	featureSet := set(features)

	sort.Strings(features)
	sort.Strings(required)

	take := func(sl *[]string) string {
		s := (*sl)[0]
		*sl = (*sl)[1:]
		return s
	}

	for len(required) > 0 || len(features) > 0 {
		switch {
		case len(features) == 0 || (len(required) > 0 && required[0] < features[0]):
			feature := take(&required)
			if exceptionSet[feature] {
				fmt.Fprintf(w, "~%s\n", feature)
			} else if featureSet[featureWithoutContext(feature)] {
				// okay.
			} else {
				fmt.Fprintf(w, "-%s\n", feature)
				ok = false // broke compatibility
			}
		case len(required) == 0 || (len(features) > 0 && required[0] > features[0]):
			newFeature := take(&features)
			if optionalSet[newFeature] {
				// Known added feature to the upcoming release.
				// Delete it from the map so we can detect any upcoming features
				// which were never seen.  (so we can clean up the nextFile)
				delete(optionalSet, newFeature)
			} else {
				fmt.Fprintf(w, "+%s\n", newFeature)
				if !allowNew {
					ok = false // we're in lock-down mode for next release
				}
			}
		default:
			take(&required)
			take(&features)
		}
	}

	// In next file, but not in API.
	var missing []string
	for feature := range optionalSet {
		missing = append(missing, feature)
	}
	sort.Strings(missing)
	for _, feature := range missing {
		fmt.Fprintf(w, "±%s\n", feature)
	}
	return
}

func fileFeatures(filename string) []string {
	bs, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatalf("Error reading file %s: %v", filename, err)
	}
	text := strings.TrimSpace(string(bs))
	if text == "" {
		return nil
	}
	return strings.Split(text, "\n")
}

func isExtract(name string) bool {
	if apiAlldecls {
		return true
	}
	return ast.IsExported(name)
}

// pkgSymbol represents a symbol in a package
type pkgSymbol struct {
	pkg    string // "net/http"
	symbol string // "RoundTripper"
}

//expression kind
type Kind int

const (
	KindBuiltin Kind = iota
	KindPackage
	KindImport
	KindVar
	KindConst
	KindInterface
	KindParam
	KindStruct
	KindMethod
	KindField
	KindType
	KindFunc
	KindChan
	KindArray
	KindMap
	KindSlice
	KindLabel
	KindBranch
)

func (k Kind) String() string {
	switch k {
	case KindBuiltin:
		return "builtin"
	case KindPackage:
		return "package"
	case KindImport:
		return "import"
	case KindVar:
		return "var"
	case KindConst:
		return "const"
	case KindParam:
		return "param"
	case KindInterface:
		return "interface"
	case KindStruct:
		return "struct"
	case KindMethod:
		return "method"
	case KindField:
		return "field"
	case KindType:
		return "type"
	case KindFunc:
		return "func"
	case KindChan:
		return "chan"
	case KindMap:
		return "map"
	case KindArray:
		return "array"
	case KindSlice:
		return "slice"
	case KindLabel:
		return "label"
	case KindBranch:
		return "branch"
	}
	return fmt.Sprint("unknown-kind")
}

//expression type
type TypeInfo struct {
	Kind Kind
	Name string
	Type string
	X    ast.Expr
	T    ast.Expr
}

type ExprType struct {
	X ast.Expr
	T string
}

type Package struct {
	dpkg             *doc.Package
	apkg             *ast.Package
	interfaceMethods map[string]([]typeMethod)
	interfaces       map[string]*ast.InterfaceType //interface
	structs          map[string]*ast.StructType    //struct
	types            map[string]ast.Expr           //type
	functions        map[string]typeMethod         //function
	consts           map[string]*ExprType          //const => type
	vars             map[string]*ExprType          //var => type
	name             string
	dir              string
	sep              string
	deps             []string
	features         map[string](token.Pos) // set
}

func NewPackage() *Package {
	return &Package{
		interfaceMethods: make(map[string]([]typeMethod)),
		interfaces:       make(map[string]*ast.InterfaceType),
		structs:          make(map[string]*ast.StructType),
		types:            make(map[string]ast.Expr),
		functions:        make(map[string]typeMethod),
		consts:           make(map[string]*ExprType),
		vars:             make(map[string]*ExprType),
		features:         make(map[string](token.Pos)),
		sep:              ", ",
	}
}

func (p *Package) Features() (fs []string) {
	for f, ps := range p.features {
		if apiShowpos {
			fs = append(fs, f+p.sep+strconv.Itoa(int(ps)))
		} else {
			fs = append(fs, f)
		}
	}
	sort.Strings(fs)
	return
}

func (p *Package) findType(name string) ast.Expr {
	for k, v := range p.interfaces {
		if k == name {
			return v
		}
	}
	for k, v := range p.structs {
		if k == name {
			return v
		}
	}
	for k, v := range p.types {
		if k == name {
			return v
		}
	}
	return nil
}

func funcRetType(ft *ast.FuncType, index int) ast.Expr {
	if ft.Results != nil {
		pos := 0
		for _, fi := range ft.Results.List {
			if fi.Names == nil {
				if pos == index {
					return fi.Type
				}
				pos++
			} else {
				for _ = range fi.Names {
					if pos == index {
						return fi.Type
					}
					pos++
				}
			}
		}
	}
	return nil
}

func findFunction(funcs []*doc.Func, name string) (*ast.Ident, *ast.FuncType) {
	for _, f := range funcs {
		if f.Name == name {
			return &ast.Ident{Name: name, NamePos: f.Decl.Pos()}, f.Decl.Type
		}
	}
	return nil, nil
}

func (p *Package) findSelectorType(name string) ast.Expr {
	if t, ok := p.vars[name]; ok {
		return &ast.Ident{
			NamePos: t.X.Pos(),
			Name:    t.T,
		}
	}
	if t, ok := p.consts[name]; ok {
		return &ast.Ident{
			NamePos: t.X.Pos(),
			Name:    t.T,
		}
	}
	if t, ok := p.functions[name]; ok {
		return t.ft
	}
	for k, v := range p.structs {
		if k == name {
			return &ast.Ident{
				NamePos: v.Pos(),
				Name:    name,
			}
		}
	}
	for k, v := range p.interfaces {
		if k == name {
			return &ast.Ident{
				NamePos: v.Pos(),
				Name:    name,
			}
		}
	}
	for k, v := range p.types {
		if k == name {
			return v
		}
	}
	return nil
}

func (p *Package) findCallFunc(name string) ast.Expr {
	if fn, ok := p.functions[name]; ok {
		return fn.ft
	}
	if s, ok := p.structs[name]; ok {
		return s
	}
	if t, ok := p.types[name]; ok {
		return t
	}
	if v, ok := p.vars[name]; ok {
		if strings.HasPrefix(v.T, "func(") {
			e, err := parser.ParseExpr(v.T + "{}")
			if err == nil {
				return e
			}
		}
	}
	return nil
}

func (p *Package) findCallType(name string, index int) ast.Expr {
	if fn, ok := p.functions[name]; ok {
		return funcRetType(fn.ft, index)
	}
	if s, ok := p.structs[name]; ok {
		return &ast.Ident{
			NamePos: s.Pos(),
			Name:    name,
		}
	}
	if t, ok := p.types[name]; ok {
		return &ast.Ident{
			NamePos: t.Pos(),
			Name:    name,
		}
	}
	return nil
}

func (p *Package) findMethod(typ, name string) (*ast.Ident, *ast.FuncType) {
	if t, ok := p.interfaces[typ]; ok && t.Methods != nil {
		for _, fd := range t.Methods.List {
			switch ft := fd.Type.(type) {
			case *ast.FuncType:
				for _, ident := range fd.Names {
					if ident.Name == name {
						return ident, ft
					}
				}
			}
		}
	}
	for k, v := range p.interfaceMethods {
		if k == typ {
			for _, m := range v {
				if m.name == name {
					return &ast.Ident{Name: name, NamePos: m.pos}, m.ft
				}
			}
		}
	}
	if p.dpkg == nil {
		return nil, nil
	}
	for _, t := range p.dpkg.Types {
		if t.Name == typ {
			return findFunction(t.Methods, name)
		}
	}
	return nil, nil
}

type Walker struct {
	context *build.Context
	fset    *token.FileSet
	scope   []string
	//	features        map[string](token.Pos) // set
	lastConstType   string
	curPackageName  string
	sep             string
	ctxName         string
	curPackage      *Package
	constDep        map[string]*ExprType // key's const identifier has type of future value const identifier
	packageState    map[string]loadState
	packageMap      map[string]*Package
	interfaces      map[pkgSymbol]*ast.InterfaceType
	selectorFullPkg map[string]string // "http" => "net/http", updated by imports
	wantedPkg       map[string]bool   // packages requested on the command line
	cursorInfo      *CursorInfo
	localvar        map[string]*ExprType
}

func NewWalker() *Walker {
	return &Walker{
		fset: token.NewFileSet(),
		//		features:        make(map[string]token.Pos),
		packageState:    make(map[string]loadState),
		interfaces:      make(map[pkgSymbol]*ast.InterfaceType),
		packageMap:      make(map[string]*Package),
		selectorFullPkg: make(map[string]string),
		wantedPkg:       make(map[string]bool),
		localvar:        make(map[string]*ExprType),
		sep:             ", ",
	}
}

// loadState is the state of a package's parsing.
type loadState int

const (
	notLoaded loadState = iota
	loading
	loaded
)

func (w *Walker) Features(ctx string) (fs []string) {
	for pkg, p := range w.packageMap {
		if w.wantedPkg[p.name] {
			if ctx == "" || strings.HasPrefix(pkg, ctx) {
				fs = append(fs, p.Features()...)
			}
		}
	}
	sort.Strings(fs)
	return
}

// fileDeps returns the imports in a file.
func fileDeps(f *ast.File) (pkgs []string) {
	for _, is := range f.Imports {
		fpkg, err := strconv.Unquote(is.Path.Value)
		if err != nil {
			log.Fatalf("error unquoting import string %q: %v", is.Path.Value, err)
		}
		if fpkg != "C" {
			pkgs = append(pkgs, fpkg)
		}
	}
	return
}

func (w *Walker) findPackage(pkg string) *Package {
	if full, ok := w.selectorFullPkg[pkg]; ok {
		if w.ctxName != "" {
			ctxName := w.ctxName + full
			for k, v := range w.packageMap {
				if k == ctxName {
					return v
				}
			}
		}
		for k, v := range w.packageMap {
			if k == full {
				return v
			}
		}
	}
	return nil
}

func (w *Walker) findPackageOSArch(pkg string) *Package {
	if full, ok := w.selectorFullPkg[pkg]; ok {
		ctxName := osArchName(w.context) + ":" + full
		for k, v := range w.packageMap {
			if k == ctxName {
				return v
			}
		}
	}
	return nil
}

// WalkPackage walks all files in package `name'.
// WalkPackage does nothing if the package has already been loaded.

func (w *Walker) WalkPackage(pkg string) {
	if build.IsLocalImport(pkg) {
		wd, err := os.Getwd()
		if err != nil {
			if apiVerbose {
				log.Println(err)
			}
			return
		}
		dir := filepath.Clean(filepath.Join(wd, pkg))
		bp, err := w.context.ImportDir(dir, 0)
		if err != nil {
			if apiVerbose {
				log.Println(err)
			}
			return
		}
		if w.wantedPkg[pkg] == true {
			w.wantedPkg[bp.Name] = true
			delete(w.wantedPkg, pkg)
		}
		if w.cursorInfo != nil && w.cursorInfo.pkg == pkg {
			w.cursorInfo.pkg = bp.Name
		}
		w.WalkPackageDir(bp.Name, bp.Dir, bp)
	} else if filepath.IsAbs(pkg) {
		bp, err := build.ImportDir(pkg, 0)
		if err != nil {
			if apiVerbose {
				log.Println(err)
			}
		}
		if w.wantedPkg[pkg] == true {
			w.wantedPkg[bp.Name] = true
			delete(w.wantedPkg, pkg)
		}
		if w.cursorInfo != nil && w.cursorInfo.pkg == pkg {
			w.cursorInfo.pkg = bp.Name
		}

		w.WalkPackageDir(bp.Name, bp.Dir, bp)
	} else {
		bp, err := build.Import(pkg, "", build.FindOnly)
		if err != nil {
			if apiVerbose {
				log.Println(err)
			}
			return
		}
		w.WalkPackageDir(pkg, bp.Dir, nil)
	}
}

func (w *Walker) WalkPackageDir(name string, dir string, bp *build.Package) {
	ctxName := w.ctxName + name
	curName := name
	switch w.packageState[ctxName] {
	case loading:
		log.Fatalf("import cycle loading package %q?", name)
		return
	case loaded:
		return
	}
	w.packageState[ctxName] = loading
	w.selectorFullPkg[name] = name

	defer func() {
		w.packageState[ctxName] = loaded
	}()

	sname := name[strings.LastIndexAny(name, ".-/\\")+1:]

	apkg := &ast.Package{
		Files: make(map[string]*ast.File),
	}
	if bp == nil {
		bp, _ = w.context.ImportDir(dir, 0)
	}
	if bp == nil {
		return
	}
	if w.ctxName != "" {
		isCgo := (len(bp.CgoFiles) > 0) && w.context.CgoEnabled
		if isCgo {
			curName = ctxName
		} else {
			isOSArch := false
			for _, file := range bp.GoFiles {
				if isOSArchFile(w.context, file) {
					isOSArch = true
					break
				}
			}
			var p *Package
			if isOSArch {
				curName = osArchName(w.context) + ":" + name
				p = w.findPackageOSArch(name)
			} else {
				curName = name
				p = w.findPackage(name)
			}
			if p != nil {
				if apiImportParser {
					for _, dep := range p.deps {
						if _, ok := w.packageState[dep]; ok {
							continue
						}
						w.WalkPackage(dep)
					}
				}
				w.packageMap[ctxName] = p
				return
			}
		}
	}

	files := append(append([]string{}, bp.GoFiles...), bp.CgoFiles...)

	if w.cursorInfo != nil && w.cursorInfo.pkg == name {
		files = append(files, bp.TestGoFiles...)
		for _, v := range bp.XTestGoFiles {
			if v == w.cursorInfo.file {
				var xbp build.Package
				xbp.Name = name + "_test"
				xbp.GoFiles = append(xbp.GoFiles, bp.XTestGoFiles...)
				w.cursorInfo.pkg = xbp.Name
				w.WalkPackageDir(xbp.Name, dir, &xbp)
				break
			}
		}
	}

	if len(files) == 0 {
		if apiVerbose {
			log.Println("no Go source files in", bp.Dir)
		}
		return
	}
	var deps []string

	for _, file := range files {
		var src interface{} = nil
		if w.cursorInfo != nil &&
			w.cursorInfo.pkg == name &&
			w.cursorInfo.file == file &&
			w.cursorInfo.std {
			src = w.cursorInfo.src
		}
		f, err := parser.ParseFile(w.fset, filepath.Join(dir, file), src, 0)
		if err != nil {
			if apiVerbose {
				log.Printf("error parsing package %s, file %s: %v", name, file, err)
			}
		}

		if sname != f.Name.Name {
			continue
		}
		apkg.Files[file] = f
		if apiImportParser {
			deps = fileDeps(f)
			for _, dep := range deps {
				if _, ok := w.packageState[dep]; ok {
					continue
				}
				w.WalkPackage(dep)
			}
		}
		if apiShowpos && w.wantedPkg[name] {
			tf := w.fset.File(f.Pos())
			if tf != nil {
				fmt.Printf("pos %s%s%s%s%d%s%d\n", name, w.sep, filepath.Join(dir, file), w.sep, tf.Base(), w.sep, tf.Size())
			}
		}
	}
	/* else {
		fdir, err := os.Open(dir)
		if err != nil {
			log.Fatalln(err)
		}
		infos, err := fdir.Readdir(-1)
		fdir.Close()
		if err != nil {
			log.Fatalln(err)
		}

		for _, info := range infos {
			if info.IsDir() {
				continue
			}
			file := info.Name()
			if strings.HasPrefix(file, "_") || strings.HasSuffix(file, "_test.go") {
				continue
			}
			if strings.HasSuffix(file, ".go") {
				f, err := parser.ParseFile(w.fset, filepath.Join(dir, file), nil, 0)
				if err != nil {
					if apiVerbose {
						log.Printf("error parsing package %s, file %s: %v", name, file, err)
					}
					continue
				}
				if f.Name.Name != sname {
					continue
				}

				apkg.Files[file] = f
				if apiImportParser {
					for _, dep := range fileDeps(f) {
						w.WalkPackage(dep)
					}
				}
				if apiShowpos && w.wantedPkg[name] {
					tf := w.fset.File(f.Pos())
					if tf != nil {
						fmt.Printf("pos %s%s%s%s%d:%d\n", name, w.sep, filepath.Join(dir, file), w.sep, tf.Base(), tf.Base()+tf.Size())
					}
				}
			}
		}
	}*/
	if curName != ctxName {
		w.packageState[curName] = loading

		defer func() {
			w.packageState[curName] = loaded
		}()
	}

	if apiVerbose {
		log.Printf("package %s => %s, %v", ctxName, curName, w.wantedPkg[curName])
	}
	pop := w.pushScope("pkg " + name)
	defer pop()

	w.curPackageName = curName
	w.constDep = map[string]*ExprType{}
	w.curPackage = NewPackage()
	w.curPackage.apkg = apkg
	w.curPackage.name = name
	w.curPackage.dir = dir
	w.curPackage.deps = deps
	w.curPackage.sep = w.sep
	w.packageMap[curName] = w.curPackage
	w.packageMap[ctxName] = w.curPackage

	for _, afile := range apkg.Files {
		w.recordTypes(afile)
	}

	// Register all function declarations first.
	for _, afile := range apkg.Files {
		for _, di := range afile.Decls {
			if d, ok := di.(*ast.FuncDecl); ok {
				if !w.isExtract(d.Name.Name) {
					continue
				}
				w.peekFuncDecl(d)
			}
		}
	}

	for _, afile := range apkg.Files {
		w.walkFile(afile)
	}

	w.resolveConstantDeps()

	if w.cursorInfo != nil && w.cursorInfo.pkg == name {
		for k, v := range apkg.Files {
			if k == w.cursorInfo.file {
				f := w.fset.File(v.Pos())
				if f == nil {
					log.Fatalf("error fset postion %v", v.Pos())
				}
				info, err := w.lookupFile(v, token.Pos(f.Base())+w.cursorInfo.pos-1)
				if err != nil {
					log.Fatalln("lookup error,", err)
				} else {
					if info != nil && info.Kind == KindImport {
						for _, is := range v.Imports {
							fpath, err := strconv.Unquote(is.Path.Value)
							if err == nil {
								if info.Name == path.Base(fpath) {
									info.T = is.Path
								}
							}
						}
					}
					w.cursorInfo.info = info
				}
				break
			}
		}
		return
	}

	// Now that we're done walking types, vars and consts
	// in the *ast.Package, use go/doc to do the rest
	// (functions and methods). This is done here because
	// go/doc is destructive.  We can't use the
	// *ast.Package after this.
	var mode doc.Mode
	if apiAllmethods {
		mode |= doc.AllMethods
	}
	if apiAlldecls && w.wantedPkg[w.ctxName] {
		mode |= doc.AllDecls
	}

	dpkg := doc.New(apkg, name, mode)
	w.curPackage.dpkg = dpkg

	if w.wantedPkg[name] != true {
		return
	}

	for _, t := range dpkg.Types {
		// Move funcs up to the top-level, not hiding in the Types.
		dpkg.Funcs = append(dpkg.Funcs, t.Funcs...)

		for _, m := range t.Methods {
			w.walkFuncDecl(m.Decl)
		}
	}

	for _, f := range dpkg.Funcs {
		w.walkFuncDecl(f.Decl)
	}
}

// pushScope enters a new scope (walking a package, type, node, etc)
// and returns a function that will leave the scope (with sanity checking
// for mismatched pushes & pops)
func (w *Walker) pushScope(name string) (popFunc func()) {
	w.scope = append(w.scope, name)
	return func() {
		if len(w.scope) == 0 {
			log.Fatalf("attempt to leave scope %q with empty scope list", name)
		}
		if w.scope[len(w.scope)-1] != name {
			log.Fatalf("attempt to leave scope %q, but scope is currently %#v", name, w.scope)
		}
		w.scope = w.scope[:len(w.scope)-1]
	}
}

func (w *Walker) recordTypes(file *ast.File) {
	cur := w.curPackage
	for _, di := range file.Decls {
		switch d := di.(type) {
		case *ast.GenDecl:
			switch d.Tok {
			case token.TYPE:
				for _, sp := range d.Specs {
					ts := sp.(*ast.TypeSpec)
					name := ts.Name.Name
					switch t := ts.Type.(type) {
					case *ast.InterfaceType:
						if isExtract(name) {
							w.noteInterface(name, t)
						}
						cur.interfaces[name] = t
					case *ast.StructType:
						cur.structs[name] = t
					default:
						cur.types[name] = ts.Type
					}
				}
			}
		}
	}
}

func inRange(node ast.Node, p token.Pos) bool {
	if node == nil {
		return false
	}
	return p >= node.Pos() && p <= node.End()
}

func (w *Walker) lookupLabel(body *ast.BlockStmt, name string) (*TypeInfo, error) {
	for _, stmt := range body.List {
		switch v := stmt.(type) {
		case *ast.BlockStmt:
			return w.lookupLabel(v, name)
		case *ast.LabeledStmt:
			return &TypeInfo{Kind: KindLabel, Name: v.Label.Name, Type: "branch", T: v.Label}, nil
		}
	}
	return nil, nil
}

func (w *Walker) lookupFile(file *ast.File, p token.Pos) (*TypeInfo, error) {
	if inRange(file.Name, p) {
		return &TypeInfo{Kind: KindPackage, X: file.Name, Name: file.Name.Name, Type: file.Name.Name, T: file.Name}, nil
	}
	for _, di := range file.Decls {
		switch d := di.(type) {
		case *ast.GenDecl:
			if inRange(d, p) {
				return w.lookupDecl(d, p, false)
			}
		case *ast.FuncDecl:
			if inRange(d, p) {
				info, err := w.lookupDecl(d, p, false)
				if info != nil && info.Kind == KindBranch {
					return w.lookupLabel(d.Body, info.Name)
				}
				return info, err
			}
			if d.Body != nil && inRange(d.Body, p) {
				return w.lookupStmt(d.Body, p)
			}
		default:
			return nil, fmt.Errorf("un parser decl %T", di)
		}
	}
	return nil, fmt.Errorf("un find cursor %v", w.fset.Position(p))
}

func (w *Walker) isExtract(name string) bool {
	if w.wantedPkg[w.curPackageName] || apiAlldecls {
		return true
	}
	return ast.IsExported(name)
}

func (w *Walker) isType(typ string) *ExprType {
	pos := strings.Index(typ, ".")
	if pos != -1 {
		pkg := typ[:pos]
		typ = typ[pos+1:]
		if p := w.findPackage(pkg); p != nil {
			if t, ok := p.types[typ]; ok {
				if r := w.isType(typ); r != nil {
					return r
				}
				return &ExprType{X: t, T: w.pkgRetType(pkg, w.nodeString(t))}
			}
		}
		return nil
	}
	if t, ok := w.curPackage.types[typ]; ok {
		if r := w.isType(w.nodeString(t)); r != nil {
			return r
		}
		return &ExprType{X: t, T: w.nodeString(t)}
	}
	return nil
}

func (w *Walker) lookupStmt(vi ast.Stmt, p token.Pos) (*TypeInfo, error) {
	if vi == nil {
		return nil, nil
	}
	switch v := vi.(type) {
	case *ast.BadStmt:
		//
	case *ast.EmptyStmt:
		//
	case *ast.LabeledStmt:
		if inRange(v.Label, p) {
			return &TypeInfo{Kind: KindLabel, Name: v.Label.Name}, nil
		}
		return w.lookupStmt(v.Stmt, p)
		//
	case *ast.DeclStmt:
		return w.lookupDecl(v.Decl, p, true)
	case *ast.AssignStmt:
		if len(v.Lhs) == len(v.Rhs) {
			for i := 0; i < len(v.Lhs); i++ {
				switch lt := v.Lhs[i].(type) {
				case *ast.Ident:
					typ, err := w.varValueType(v.Rhs[i], 0)
					if err == nil && v.Tok == token.DEFINE {
						w.localvar[lt.Name] = &ExprType{T: typ, X: lt}
					} else if apiVerbose {
						log.Println(err)
					}
				}
				if inRange(v.Lhs[i], p) {
					return w.lookupExprInfo(v.Lhs[i], p)
				} else if inRange(v.Rhs[i], p) {
					return w.lookupExprInfo(v.Rhs[i], p)
				}
				if fl, ok := v.Rhs[i].(*ast.FuncLit); ok {
					if inRange(fl, p) {
						return w.lookupStmt(fl.Body, p)
					}
				}
			}
		} else if len(v.Rhs) == 1 {
			for i := 0; i < len(v.Lhs); i++ {
				switch lt := v.Lhs[i].(type) {
				case *ast.Ident:
					typ, err := w.varValueType(v.Rhs[0], i)
					if err == nil && v.Tok == token.DEFINE {
						w.localvar[lt.Name] = &ExprType{T: typ, X: lt}
					} else if apiVerbose {
						log.Println(err)
					}
				}
				if inRange(v.Lhs[i], p) {
					return w.lookupExprInfo(v.Lhs[i], p)
				} else if inRange(v.Rhs[0], p) {
					return w.lookupExprInfo(v.Rhs[0], p)
				}
				if fl, ok := v.Rhs[0].(*ast.FuncLit); ok {
					if inRange(fl, p) {
						return w.lookupStmt(fl.Body, p)
					}
				}
			}
		}
		return nil, nil
	case *ast.ExprStmt:
		return w.lookupExprInfo(v.X, p)
	case *ast.BlockStmt:
		for _, st := range v.List {
			if inRange(st, p) {
				return w.lookupStmt(st, p)
			}
			_, err := w.lookupStmt(st, p)
			if err != nil {
				log.Println(err)
			}
		}
	case *ast.IfStmt:
		if inRange(v.Init, p) {
			return w.lookupStmt(v.Init, p)
		} else {
			w.lookupStmt(v.Init, p)
		}
		if inRange(v.Cond, p) {
			return w.lookupExprInfo(v.Cond, p)
		} else if inRange(v.Body, p) {
			return w.lookupStmt(v.Body, p)
		} else if inRange(v.Else, p) {
			return w.lookupStmt(v.Else, p)
		}
	case *ast.SendStmt:
		if inRange(v.Chan, p) {
			return w.lookupExprInfo(v.Chan, p)
		} else if inRange(v.Value, p) {
			return w.lookupExprInfo(v.Value, p)
		}
	case *ast.IncDecStmt:
		return w.lookupExprInfo(v.X, p)
	case *ast.GoStmt:
		return w.lookupExprInfo(v.Call, p)
	case *ast.DeferStmt:
		return w.lookupExprInfo(v.Call, p)
	case *ast.ReturnStmt:
		for _, r := range v.Results {
			if inRange(r, p) {
				return w.lookupExprInfo(r, p)
			}
		}
	case *ast.BranchStmt:
		if inRange(v.Label, p) {
			return &TypeInfo{Kind: KindBranch, Name: v.Label.Name, Type: "label", T: v.Label}, nil
		}
		//
	case *ast.CaseClause:
		for _, r := range v.List {
			if inRange(r, p) {
				return w.lookupExprInfo(r, p)
			}
		}
		for _, body := range v.Body {
			if inRange(body, p) {
				return w.lookupStmt(body, p)
			} else {
				w.lookupStmt(body, p)
			}
		}
	case *ast.SwitchStmt:
		if inRange(v.Init, p) {
			return w.lookupStmt(v.Init, p)
		} else {
			w.lookupStmt(v.Init, p)
		}
		if inRange(v.Tag, p) {
			return w.lookupExprInfo(v.Tag, p)
		} else if inRange(v.Body, p) {
			return w.lookupStmt(v.Body, p)
		}
	case *ast.TypeSwitchStmt:
		if inRange(v.Assign, p) {
			return w.lookupStmt(v.Assign, p)
		} else {
			w.lookupStmt(v.Assign, p)
		}
		if inRange(v.Init, p) {
			return w.lookupStmt(v.Init, p)
		} else {
			w.lookupStmt(v.Init, p)
		}
		var vs string
		if as, ok := v.Assign.(*ast.AssignStmt); ok {
			if len(as.Lhs) == 1 {
				vs = w.nodeString(as.Lhs[0])
			}
		}
		if inRange(v.Body, p) {
			for _, s := range v.Body.List {
				if inRange(s, p) {
					switch cs := s.(type) {
					case *ast.CaseClause:
						for _, r := range cs.List {
							if inRange(r, p) {
								return w.lookupExprInfo(r, p)
							} else if vs != "" {
								typ, err := w.varValueType(r, 0)
								if err == nil {
									w.localvar[vs] = &ExprType{T: typ, X: r}
								}
							}
						}
						for _, body := range cs.Body {
							if inRange(body, p) {
								return w.lookupStmt(body, p)
							} else {
								w.lookupStmt(body, p)
							}
						}
					default:
						return w.lookupStmt(cs, p)
					}
				}
			}
		}
	case *ast.CommClause:
		if inRange(v.Comm, p) {
			return w.lookupStmt(v.Comm, p)
		}
		for _, body := range v.Body {
			if inRange(body, p) {
				return w.lookupStmt(body, p)
			}
		}
	case *ast.SelectStmt:
		if inRange(v.Body, p) {
			return w.lookupStmt(v.Body, p)
		}
	case *ast.ForStmt:
		if inRange(v.Init, p) {
			return w.lookupStmt(v.Init, p)
		} else {
			w.lookupStmt(v.Init, p)
		}
		if inRange(v.Cond, p) {
			return w.lookupExprInfo(v.Cond, p)
		} else if inRange(v.Body, p) {
			return w.lookupStmt(v.Body, p)
		} else if inRange(v.Post, p) {
			return w.lookupStmt(v.Post, p)
		}
	case *ast.RangeStmt:
		if inRange(v.X, p) {
			return w.lookupExprInfo(v.X, p)
		} else if inRange(v.Key, p) {
			return &TypeInfo{Kind: KindBuiltin, Name: w.nodeString(v.Key), Type: "int"}, nil
		} else if inRange(v.Value, p) {
			typ, err := w.lookupExprInfo(v.X, p)
			if typ != nil {
				typ.Name = w.nodeString(v.Value)
				return typ, err
			}
		} else {
			typ, err := w.varValueType(v.X, 0)
			//check is type
			if t := w.isType(typ); t != nil {
				typ = t.T
			}
			if err == nil {
				var kt, vt string
				if strings.HasPrefix(typ, "[]") {
					kt = "int"
					vt = typ[2:]
				} else if strings.HasPrefix(typ, "map[") {
					node, err := parser.ParseExpr(typ + "{}")
					if err == nil {
						if cl, ok := node.(*ast.CompositeLit); ok {
							if m, ok := cl.Type.(*ast.MapType); ok {
								kt = w.nodeString(w.namelessType(m.Key))
								vt = w.nodeString(w.namelessType(m.Value))
							}
						}
					}
				}
				if inRange(v.Key, p) {
					return &TypeInfo{Kind: KindVar, X: v.Key, Name: w.nodeString(v.Key), T: v.X, Type: kt}, nil
				} else if inRange(v.Value, p) {
					return &TypeInfo{Kind: KindVar, X: v.Value, Name: w.nodeString(v.Value), T: v.X, Type: vt}, nil
				}
				if key, ok := v.Key.(*ast.Ident); ok {
					w.localvar[key.Name] = &ExprType{T: kt, X: v.Key}
				}
				if value, ok := v.Value.(*ast.Ident); ok {
					w.localvar[value.Name] = &ExprType{T: vt, X: v.Value}
				}
			}
		}
		if inRange(v.Body, p) {
			return w.lookupStmt(v.Body, p)
		}
	}
	return nil, nil //fmt.Errorf("not lookup stmt %v %T", vi, vi)
}

func (w *Walker) lookupVar(vs *ast.ValueSpec, p token.Pos, local bool) (*TypeInfo, error) {
	if inRange(vs.Type, p) {
		return w.lookupExprInfo(vs.Type, p)
	}
	for _, v := range vs.Values {
		if inRange(v, p) {
			return w.lookupExprInfo(v, p)
		}
	}
	if vs.Type != nil {
		typ := w.nodeString(vs.Type)
		for _, ident := range vs.Names {
			if local {
				w.localvar[ident.Name] = &ExprType{T: typ, X: ident}
			}
			if inRange(ident, p) {
				return &TypeInfo{Kind: KindVar, X: ident, Name: ident.Name, T: vs.Type, Type: typ}, nil
			}
		}
	} else if len(vs.Names) == len(vs.Values) {
		for n, ident := range vs.Names {
			typ := ""
			if !local {
				if t, ok := w.curPackage.vars[ident.Name]; ok {
					typ = t.T
				}
			} else {
				typ, err := w.varValueType(vs.Values[n], n)
				if err != nil {
					if apiVerbose {
						log.Printf("unknown type of variable2 %q, type %T, error = %v, pos=%s",
							ident.Name, vs.Values[n], err, w.fset.Position(vs.Pos()))
					}
					typ = "unknown-type"
				}
				w.localvar[ident.Name] = &ExprType{T: typ, X: ident}
			}
			if inRange(ident, p) {
				return &TypeInfo{Kind: KindVar, X: ident, Name: ident.Name, T: ident, Type: typ}, nil
			}
		}
	} else if len(vs.Values) == 1 {
		for n, ident := range vs.Names {
			typ := ""
			if !local {
				if t, ok := w.curPackage.vars[ident.Name]; ok {
					typ = t.T
				}
			} else {
				typ, err := w.varValueType(vs.Values[0], n)
				if err != nil {
					if apiVerbose {
						log.Printf("unknown type of variable3 %q, type %T, error = %v, pos=%s",
							ident.Name, vs.Values[0], err, w.fset.Position(vs.Pos()))
					}
					typ = "unknown-type"
				}
				w.localvar[ident.Name] = &ExprType{T: typ, X: ident}
			}
			if inRange(ident, p) {
				return &TypeInfo{Kind: KindVar, X: ident, Name: ident.Name, T: ident, Type: typ}, nil
			}
		}
	}
	return nil, fmt.Errorf("not lookup var local:%v value:%v type:s%T", local, w.nodeString(vs), vs)
}

func (w *Walker) lookupConst(vs *ast.ValueSpec, p token.Pos, local bool) (*TypeInfo, error) {
	if inRange(vs.Type, p) {
		return w.lookupExprInfo(vs.Type, p)
	}
	for _, ident := range vs.Names {
		typ := ""
		if !local {
			if t, ok := w.curPackage.consts[ident.Name]; ok {
				typ = t.T
			}
		} else {
			litType := ""
			if vs.Type != nil {
				litType = w.nodeString(vs.Type)
			} else {
				litType = w.lastConstType
				if vs.Values != nil {
					if len(vs.Values) != 1 {
						if apiVerbose {
							log.Printf("const %q, values: %#v", ident.Name, vs.Values)
						}
						return nil, nil
					}
					var err error
					litType, err = w.constValueType(vs.Values[0])
					if err != nil {
						if apiVerbose {
							log.Printf("unknown kind in const %q (%T): %v", ident.Name, vs.Values[0], err)
						}
						litType = "unknown-type"
					}
				}
			}
			w.lastConstType = litType
			typ = litType
			w.localvar[ident.Name] = &ExprType{T: typ, X: ident}
		}
		if inRange(ident, p) {
			return &TypeInfo{Kind: KindConst, X: ident, Name: ident.Name, T: ident, Type: typ}, nil
		}
	}
	return nil, nil
}

func (w *Walker) lookupType(ts *ast.TypeSpec, p token.Pos, local bool) (*TypeInfo, error) {
	switch t := ts.Type.(type) {
	case *ast.StructType:
		if inRange(t.Fields, p) {
			for _, fd := range t.Fields.List {
				if inRange(fd.Type, p) {
					return w.lookupExprInfo(fd.Type, p)
				}
				for _, ident := range fd.Names {
					if inRange(ident, p) {
						return &TypeInfo{Kind: KindField, X: ident, Name: ts.Name.Name + "." + ident.Name, T: fd.Type, Type: w.nodeString(w.namelessType(fd.Type))}, nil
					}
				}
			}
		}
		return &TypeInfo{Kind: KindStruct, X: ts.Name, Name: ts.Name.Name, T: ts.Type, Type: "struct"}, nil
	case *ast.InterfaceType:
		if inRange(t.Methods, p) {
			for _, fd := range t.Methods.List {
				for _, ident := range fd.Names {
					if inRange(ident, p) {
						return &TypeInfo{Kind: KindMethod, X: ident, Name: ts.Name.Name + "." + ident.Name, T: ident, Type: w.nodeString(w.namelessType(fd.Type))}, nil
					}
				}
				if inRange(fd.Type, p) {
					return w.lookupExprInfo(fd.Type, p)
				}
			}
		}
		return &TypeInfo{Kind: KindInterface, X: ts.Name, Name: ts.Name.Name, T: ts.Type, Type: "interface"}, nil
	default:
		return &TypeInfo{Kind: KindType, X: ts.Name, Name: ts.Name.Name, T: ts.Type, Type: w.nodeString(w.namelessType(ts.Type))}, nil
	}
	return nil, nil
}

func (w *Walker) lookupDecl(di ast.Decl, p token.Pos, local bool) (*TypeInfo, error) {
	switch d := di.(type) {
	case *ast.GenDecl:
		switch d.Tok {
		case token.IMPORT:
			for _, sp := range d.Specs {
				is := sp.(*ast.ImportSpec)
				fpath, err := strconv.Unquote(is.Path.Value)
				if err != nil {
					return nil, err
				}
				name := path.Base(fpath)
				if is.Name != nil {
					name = is.Name.Name
				}
				if inRange(sp, p) {
					return &TypeInfo{Kind: KindImport, X: is.Name, Name: name, T: is.Name, Type: fpath}, nil
				}
			}
		case token.CONST:
			for _, sp := range d.Specs {
				if inRange(sp, p) {
					return w.lookupConst(sp.(*ast.ValueSpec), p, local)
				} else {
					w.lookupConst(sp.(*ast.ValueSpec), p, local)
				}
			}
			return nil, nil
		case token.TYPE:
			for _, sp := range d.Specs {
				if inRange(sp, p) {
					return w.lookupType(sp.(*ast.TypeSpec), p, local)
				} else {
					w.lookupType(sp.(*ast.TypeSpec), p, local)
				}
			}
		case token.VAR:
			for _, sp := range d.Specs {
				if inRange(sp, p) {
					return w.lookupVar(sp.(*ast.ValueSpec), p, local)
				} else {
					w.lookupVar(sp.(*ast.ValueSpec), p, local)
				}
			}
			return nil, nil
		default:
			return nil, fmt.Errorf("unknown token type %d %T in GenDecl", d.Tok, d)
		}
	case *ast.FuncDecl:
		if d.Type.Params != nil {
			for _, fd := range d.Type.Params.List {
				if inRange(fd, p) {
					return w.lookupExprInfo(fd.Type, p)
				}
				for _, ident := range fd.Names {
					if inRange(ident, p) {
						info, err := w.lookupExprInfo(fd.Type, p)
						if err == nil {
							return &TypeInfo{Kind: KindParam, X: ident, Name: ident.Name, T: info.T, Type: info.Type}, nil
						}
					}
					typ, err := w.varValueType(fd.Type, 0)
					if err == nil {
						w.localvar[ident.Name] = &ExprType{T: typ, X: ident}
					} else if apiVerbose {
						log.Println(err)
					}
				}
			}
		}
		if d.Type.Results != nil {
			for _, fd := range d.Type.Results.List {
				if inRange(fd, p) {
					return w.lookupExprInfo(fd.Type, p)
				}
				for _, ident := range fd.Names {
					typ, err := w.varValueType(fd.Type, 0)
					if err == nil {
						w.localvar[ident.Name] = &ExprType{T: typ, X: ident}
					}
				}
			}
		}
		if d.Recv != nil {
			for _, fd := range d.Recv.List {
				if inRange(fd, p) {
					return w.lookupExprInfo(fd.Type, p)
				}
				for _, ident := range fd.Names {
					w.localvar[ident.Name] = &ExprType{T: w.nodeString(fd.Type), X: ident}
				}
			}
		}
		if inRange(d.Body, p) {
			return w.lookupStmt(d.Body, p)
		}
		var fname = d.Name.Name
		kind := KindFunc
		if d.Recv != nil {
			recvTypeName, imp := baseTypeName(d.Recv.List[0].Type)
			if imp {
				return nil, nil
			}
			fname = recvTypeName + "." + d.Name.Name
			kind = KindMethod
		}
		return &TypeInfo{Kind: kind, X: d.Name, Name: fname, T: d.Type, Type: w.nodeString(w.namelessType(d.Type))}, nil
	default:
		return nil, fmt.Errorf("unhandled %T, %#v\n", di, di)
	}
	return nil, fmt.Errorf("not lookupDecl %v %T", w.nodeString(di), di)
}

func (w *Walker) lookupExprInfo(vi ast.Expr, p token.Pos) (*TypeInfo, error) {
	_, info, err := w.lookupExpr(vi, p)
	return info, err
}

// lookupExpr , return name,info,error
func (w *Walker) lookupExpr(vi ast.Expr, p token.Pos) (string, *TypeInfo, error) {
	if apiVerbose {
		log.Printf("lookup expr %v %T", w.nodeString(vi), vi)
	}
	switch v := vi.(type) {
	case *ast.BasicLit:
		litType, ok := varType[v.Kind]
		if !ok {
			return "", nil, fmt.Errorf("unknown basic literal kind %#v", v)
		}
		name := v.Value
		if len(name) >= 128 {
			name = name[:128] + "..."
		}
		return litType, &TypeInfo{Kind: KindBuiltin, X: v, Name: name, T: v, Type: litType}, nil
	case *ast.StarExpr:
		s, info, err := w.lookupExpr(v.X, p)
		if err != nil {
			return "", nil, err
		}
		return "*" + s, &TypeInfo{Kind: info.Kind, X: v, Name: "*" + info.Name, T: info.T, Type: "*" + info.Type}, err
	case *ast.InterfaceType:
		return "interface{}", &TypeInfo{Kind: KindInterface, X: v, Name: w.nodeString(v), T: v, Type: "interface{}"}, nil
	case *ast.Ellipsis:
		s, info, err := w.lookupExpr(v.Elt, p)
		if err != nil {
			return "", nil, err
		}
		return "[]" + s, &TypeInfo{Kind: KindArray, X: v.Elt, Name: "..." + s, T: info.T, Type: "[]" + info.Type}, nil
	case *ast.KeyValueExpr:
		if inRange(v.Key, p) {
			return w.lookupExpr(v.Key, p)
		} else if inRange(v.Value, p) {
			return w.lookupExpr(v.Value, p)
		}
	case *ast.CompositeLit:
		typ, err := w.varValueType(v.Type, 0)
		if err == nil {
			typ = strings.TrimLeft(typ, "*")
			if strings.HasPrefix(typ, "[]") {
				typ = strings.TrimLeft(typ[2:], "*")
			}
			pos := strings.Index(typ, ".")
			var pt *Package = w.curPackage
			var pkgdot string
			if pos != -1 {
				pkg := typ[:pos]
				typ = typ[pos+1:]
				pt = w.findPackage(pkg)
				if pt != nil {
					pkgdot = pkg + "."
				}
			}
			if pt != nil {
				if ss, ok := pt.structs[typ]; ok {
					for _, elt := range v.Elts {
						if inRange(elt, p) {
							if cl, ok := elt.(*ast.CompositeLit); ok {
								for _, elt := range cl.Elts {
									if inRange(elt, p) {
										if kv, ok := elt.(*ast.KeyValueExpr); ok {
											if inRange(kv.Key, p) {
												n, t := w.findStructField(ss, w.nodeString(kv.Key))
												if n != nil {
													return pkgdot + typ + "." + w.nodeString(kv.Key), &TypeInfo{Kind: KindField, X: kv.Key, Name: pkgdot + typ + "." + w.nodeString(kv.Key), T: n, Type: w.nodeString(w.namelessType(t))}, nil
												}
											} else if inRange(kv.Value, p) {
												return w.lookupExpr(kv.Value, p)
											}
										}
									}
								}
							}
							if kv, ok := elt.(*ast.KeyValueExpr); ok {
								if inRange(kv.Key, p) {
									n, t := w.findStructField(ss, w.nodeString(kv.Key))
									if n != nil {
										return typ + "." + w.nodeString(kv.Key), &TypeInfo{Kind: KindField, X: kv.Key, Name: typ + "." + w.nodeString(kv.Key), T: n, Type: w.nodeString(w.namelessType(t))}, nil
									}
								} else if inRange(kv.Value, p) {
									return w.lookupExpr(kv.Value, p)
								}
							}
						}
					}
				}
			}
		}
		for _, elt := range v.Elts {
			if inRange(elt, p) {
				return w.lookupExpr(elt, p)
			}
		}
		return w.lookupExpr(v.Type, p)
	case *ast.UnaryExpr:
		s, info, err := w.lookupExpr(v.X, p)
		return v.Op.String() + s, info, err
	case *ast.TypeAssertExpr:
		if inRange(v.X, p) {
			return w.lookupExpr(v.X, p)
		}
		return w.lookupExpr(v.Type, p)
	case *ast.BinaryExpr:
		if inRange(v.X, p) {
			return w.lookupExpr(v.X, p)
		} else if inRange(v.Y, p) {
			return w.lookupExpr(v.Y, p)
		}
		return "", nil, nil
	case *ast.CallExpr:
		for _, arg := range v.Args {
			if inRange(arg, p) {
				return w.lookupExpr(arg, p)
			}
		}
		switch ft := v.Fun.(type) {
		case *ast.Ident:
			if typ, ok := w.localvar[ft.Name]; ok {
				return ft.Name, &TypeInfo{Kind: KindVar, X: ft, Name: ft.Name, T: typ.X, Type: typ.T}, nil
			}
			if typ, ok := w.curPackage.vars[ft.Name]; ok {
				return ft.Name, &TypeInfo{Kind: KindVar, X: v, Name: ft.Name, T: typ.X, Type: typ.T}, nil
			}
			if typ, ok := w.curPackage.functions[ft.Name]; ok {
				return ft.Name, &TypeInfo{Kind: KindFunc, X: ft, Name: ft.Name, T: typ.ft, Type: typ.sig}, nil
			}
			if typ, ok := w.curPackage.interfaces[ft.Name]; ok {
				return ft.Name, &TypeInfo{Kind: KindInterface, X: ft, Name: ft.Name, T: typ, Type: w.nodeString(w.namelessType(typ))}, nil
			}
			if typ, ok := w.curPackage.interfaces[ft.Name]; ok {
				return ft.Name, &TypeInfo{Kind: KindInterface, X: ft, Name: ft.Name, T: typ, Type: w.nodeString(w.namelessType(typ))}, nil
			}
			if typ, ok := w.curPackage.structs[ft.Name]; ok {
				return ft.Name, &TypeInfo{Kind: KindStruct, X: ft, Name: ft.Name, T: typ, Type: w.nodeString(w.namelessType(typ))}, nil
			}
			if typ, ok := w.curPackage.types[ft.Name]; ok {
				return ft.Name, &TypeInfo{Kind: KindType, X: ft, Name: ft.Name, T: typ, Type: w.nodeString(w.namelessType(typ))}, nil
			}
			if isBuiltinType(ft.Name) {
				return ft.Name, &TypeInfo{Kind: KindBuiltin, X: ft, Name: ft.Name}, nil
			}
			return "", nil, fmt.Errorf("lookup unknown ident %v", v)
		case *ast.FuncLit:
			if inRange(ft.Body, p) {
				info, err := w.lookupStmt(ft.Body, p)
				if err == nil {
					return "", info, nil
				}
				return "", nil, err
			}
			return w.lookupExpr(ft.Type, p)
		case *ast.ParenExpr:
			return w.lookupExpr(ft.X, p)
		case *ast.SelectorExpr:
			switch st := ft.X.(type) {
			case *ast.Ident:
				if inRange(st, p) {
					return w.lookupExpr(st, p)
				}
				s, info, err := w.lookupExpr(st, p)
				if err != nil {
					return "", nil, err
				}
				typ := info.Type
				if typ == "" {
					typ = s
				}
				fname := typ + "." + ft.Sel.Name
				typ = strings.TrimLeft(typ, "*")
				if fn, ok := w.curPackage.functions[fname]; ok {
					return fname, &TypeInfo{Kind: KindMethod, X: st, Name: fname, T: fn.ft, Type: w.nodeString(w.namelessType(fn.ft))}, nil
				}
				info, e := w.lookupFunction(typ, ft.Sel.Name)
				if e != nil {
					return "", nil, e
				}
				return fname, info, nil
			case *ast.SelectorExpr:
				if inRange(st.X, p) {
					return w.lookupExpr(st.X, p)
				}
				if inRange(st, p) {
					return w.lookupExpr(st, p)
				}
				typ, err := w.varValueType(st, 0)
				if err != nil {
					return "", nil, err
				}
				/*
					typ = strings.TrimLeft(typ, "*")
					if t := w.curPackage.findType(typ); t != nil {
						if ss, ok := t.(*ast.StructType); ok {
							for _, fi := range ss.Fields.List {
								for _, n := range fi.Names {
									if n.Name == st.Sel.Name {
										//return fname, &TypeInfo{Kind: KindField, X: n, Name: fname, T: fi.Type, Type: w.nodeString(w.namelessType(fi.Type))}, nil
										typ = w.nodeString(w.namelessType(fi.Type))
									}
								}
							}
						}
					}
				*/
				info, e := w.lookupFunction(typ, ft.Sel.Name)
				if e != nil {
					return "", nil, e
				}
				return typ + "." + st.Sel.Name, info, nil
			case *ast.CallExpr:
				if inRange(st, p) {
					return w.lookupExpr(st, p)
				}
				if info, err := w.lookupExprInfo(st, p); err == nil {
					if fn, ok := info.X.(*ast.FuncType); ok {
						if fn.Results.NumFields() == 1 {
							info, err := w.lookupFunction(w.nodeString(fn.Results.List[0].Type), ft.Sel.Name)
							if err == nil {
								return info.Name, info, err
							}
							return "", nil, err
						}
					}
				}
				//w.lookupFunction(w.nodeString(info.X))
				typ, err := w.varValueType(st, 0)
				if err != nil {
					return "", nil, err
				}
				info, e := w.lookupFunction(typ, ft.Sel.Name)
				if e != nil {
					return "", nil, e
				}
				return typ + "." + ft.Sel.Name, info, nil
			case *ast.TypeAssertExpr:
				if inRange(st.X, p) {
					return w.lookupExpr(st.X, p)
				}
				typ := w.nodeString(w.namelessType(st.Type))
				info, e := w.lookupFunction(typ, ft.Sel.Name)
				if e != nil {
					return "", nil, e
				}
				return typ + "." + ft.Sel.Name, info, nil
			default:
				return "", nil, fmt.Errorf("not find select %v %T", v, st)
			}
		}
		return "", nil, fmt.Errorf("not find call %v %T", w.nodeString(v), v.Fun)
	case *ast.SelectorExpr:
		switch st := v.X.(type) {
		case *ast.Ident:
			if inRange(st, p) {
				return w.lookupExpr(st, p)
			}
			info, err := w.lookupSelector(st.Name, v.Sel.Name)
			if err != nil {
				return "", nil, err
			}
			return st.Name + "." + v.Sel.Name, info, nil
			//		case *ast.CallExpr:
			//			typ, err := w.varValueType(v.X, index)
			//			if err == nil {
			//				if strings.HasPrefix(typ, "*") {
			//					typ = typ[1:]
			//				}
			//				t := w.curPackage.findType(typ)
			//				if st, ok := t.(*ast.StructType); ok {
			//					for _, fi := range st.Fields.List {
			//						for _, n := range fi.Names {
			//							if n.Name == v.Sel.Name {
			//								return w.varValueType(fi.Type, index)
			//							}
			//						}
			//					}
			//				}
			//			}
		case *ast.SelectorExpr:
			if inRange(st.X, p) {
				return w.lookupExpr(st.X, p)
			}

			if inRange(st, p) {
				return w.lookupExpr(st, p)
			}

			typ, err := w.varValueType(st, 0)
			if err == nil {
				info, err := w.lookupSelector(typ, v.Sel.Name)
				if err != nil {
					return "", nil, err
				}
				return typ + v.Sel.Name, info, nil
			}
			//		case *ast.IndexExpr:
			//			typ, err := w.varValueType(st.X, 0)
			//			log.Println(typ, err)
			//			if err == nil {
			//				if strings.HasPrefix(typ, "[]") {
			//					return w.varSelectorType(typ[2:], v.Sel.Name)
			//				}
			//			}
		}
		return "", nil, fmt.Errorf("unknown lookup selector expr: %T %s.%s", v.X, w.nodeString(v.X), v.Sel)

		//		s, info, err := w.lookupExpr(v.X, p)
		//		if err != nil {
		//			return "", "", err
		//		}
		//		if strings.HasPrefix(s, "*") {
		//			s = s[1:]
		//		}
		//		if inRange(v.X, p) {
		//			return s, info, err
		//		}
		//		t := w.curPackage.findType(s)
		//		fname := s + "." + v.Sel.Name
		//		if st, ok := t.(*ast.StructType); ok {
		//			for _, fi := range st.Fields.List {
		//				for _, n := range fi.Names {
		//					if n.Name == v.Sel.Name {
		//						return fname, fmt.Sprintf("var,%s,%s,%s", fname, w.nodeString(w.namelessType(fi.Type)), w.fset.Position(n.Pos())), nil
		//					}
		//				}
		//			}
		//		}
		//		log.Println(">>", s)
		//		info, e := w.lookupSelector(s, v.Sel.Name)
		//		return fname, info, e
	case *ast.Ident:
		if typ, ok := w.localvar[v.Name]; ok {
			return typ.T, &TypeInfo{Kind: KindVar, X: v, Name: v.Name, T: typ.X, Type: typ.T}, nil
		}
		if typ, ok := w.curPackage.interfaces[v.Name]; ok {
			return v.Name, &TypeInfo{Kind: KindInterface, X: v, Name: v.Name, T: typ, Type: "interface"}, nil
		}
		if typ, ok := w.curPackage.structs[v.Name]; ok {
			return v.Name, &TypeInfo{Kind: KindStruct, X: v, Name: v.Name, T: typ, Type: "struct"}, nil
		}
		if typ, ok := w.curPackage.types[v.Name]; ok {
			return v.Name, &TypeInfo{Kind: KindType, X: v, Name: v.Name, T: typ, Type: v.Name}, nil
		}
		if typ, ok := w.curPackage.vars[v.Name]; ok {
			return v.Name, &TypeInfo{Kind: KindVar, X: v, Name: v.Name, T: typ.X, Type: typ.T}, nil
		}
		if typ, ok := w.curPackage.consts[v.Name]; ok {
			return v.Name, &TypeInfo{Kind: KindConst, X: v, Name: v.Name, T: typ.X, Type: typ.T}, nil
		}
		if typ, ok := w.curPackage.functions[v.Name]; ok {
			return v.Name, &TypeInfo{Kind: KindFunc, X: typ.ft, Name: v.Name, T: typ.ft, Type: typ.sig}, nil
		}
		if p := w.findPackage(v.Name); p != nil {
			return v.Name, &TypeInfo{Kind: KindImport, X: v, Name: v.Name, Type: p.name}, nil
		}
		if isBuiltinType(v.Name) {
			return v.Name, &TypeInfo{Kind: KindBuiltin, Name: v.Name}, nil
		}
		return "", nil, fmt.Errorf("lookup unknown ident %v", v)
		//return v.Name, &TypeInfo{Kind: KindVar, X: v, Name: v.Name, T: v, Type: v.Name}, nil
	case *ast.IndexExpr:
		if inRange(v.Index, p) {
			return w.lookupExpr(v.Index, p)
		}
		return w.lookupExpr(v.X, p)
	case *ast.ParenExpr:
		return w.lookupExpr(v.X, p)
	case *ast.FuncLit:
		if inRange(v.Type, p) {
			return w.lookupExpr(v.Type, p)
		} else {
			w.lookupExpr(v.Type, p)
		}
		typ, err := w.varValueType(v.Type, 0)
		if err != nil {
			return "", nil, err
		}
		info, e := w.lookupStmt(v.Body, p)
		if e != nil {
			return "", nil, err
		}
		return typ, info, nil
	case *ast.FuncType:
		if v.Params != nil {
			for _, fd := range v.Params.List {
				if inRange(fd, p) {
					return w.lookupExpr(fd.Type, p)
				}
				for _, ident := range fd.Names {
					typ, err := w.varValueType(fd.Type, 0)
					if err == nil {
						w.localvar[ident.Name] = &ExprType{T: typ, X: ident}
					}
				}
			}
		}
		if v.Results != nil {
			for _, fd := range v.Results.List {
				if inRange(fd, p) {
					return w.lookupExpr(fd.Type, p)
				}
				for _, ident := range fd.Names {
					typ, err := w.varValueType(fd.Type, 0)
					if err == nil {
						w.localvar[ident.Name] = &ExprType{T: typ, X: ident}
					}
				}
			}
		}
		return "", nil, nil
	case *ast.ArrayType:
		s, info, err := w.lookupExpr(v.Elt, p)
		if err != nil {
			return "", nil, err
		}
		return "[]" + s, &TypeInfo{Kind: KindArray, Name: "[]" + info.Name, Type: "[]" + info.Type, T: info.T}, nil
	case *ast.SliceExpr:
		if inRange(v.High, p) {
			return w.lookupExpr(v.High, p)
		} else if inRange(v.Low, p) {
			return w.lookupExpr(v.Low, p)
		}
		return w.lookupExpr(v.X, p)
	case *ast.MapType:
		if inRange(v.Key, p) {
			return w.lookupExpr(v.Key, p)
		} else if inRange(v.Value, p) {
			return w.lookupExpr(v.Value, p)
		}
		typ, err := w.varValueType(v, 0)
		if err != nil {
			return "", nil, err
		}
		return typ, &TypeInfo{Kind: KindMap, X: v, Name: w.nodeString(v), T: v, Type: typ}, nil
	case *ast.ChanType:
		if inRange(v.Value, p) {
			return w.lookupExpr(v.Value, p)
		}
		typ, err := w.varValueType(v, 0)
		if err != nil {
			return "", nil, err
		}
		return typ, &TypeInfo{Kind: KindChan, X: v, Name: w.nodeString(v), T: v, Type: typ}, nil
	default:
		return "", nil, fmt.Errorf("not lookupExpr %v %T", w.nodeString(v), v)
	}
	return "", nil, fmt.Errorf("not lookupExpr %v %T", w.nodeString(vi), vi)
}

func (w *Walker) walkFile(file *ast.File) {
	// Not entering a scope here; file boundaries aren't interesting.
	for _, di := range file.Decls {
		switch d := di.(type) {
		case *ast.GenDecl:
			switch d.Tok {
			case token.IMPORT:
				for _, sp := range d.Specs {
					is := sp.(*ast.ImportSpec)
					fpath, err := strconv.Unquote(is.Path.Value)
					if err != nil {
						log.Fatal(err)
					}
					//name := path.Base(fpath)
					name := fpath
					if i := strings.LastIndexAny(name, ".-/\\"); i > 0 {
						name = name[i+1:]
					}
					if is.Name != nil {
						name = is.Name.Name
					}
					w.selectorFullPkg[name] = fpath
				}
			case token.CONST:
				for _, sp := range d.Specs {
					w.walkConst(sp.(*ast.ValueSpec))
				}
			case token.TYPE:
				for _, sp := range d.Specs {
					w.walkTypeSpec(sp.(*ast.TypeSpec))
				}
			case token.VAR:
				for _, sp := range d.Specs {
					w.walkVar(sp.(*ast.ValueSpec))
				}
			default:
				log.Fatalf("unknown token type %d in GenDecl", d.Tok)
			}
		case *ast.FuncDecl:
			// Ignore. Handled in subsequent pass, by go/doc.
		default:
			log.Printf("unhandled %T, %#v\n", di, di)
			printer.Fprint(os.Stderr, w.fset, di)
			os.Stderr.Write([]byte("\n"))
		}
	}
}

var constType = map[token.Token]string{
	token.INT:    "ideal-int",
	token.FLOAT:  "ideal-float",
	token.STRING: "ideal-string",
	token.CHAR:   "ideal-char",
	token.IMAG:   "ideal-imag",
}

var varType = map[token.Token]string{
	token.INT:    "int",
	token.FLOAT:  "float64",
	token.STRING: "string",
	token.CHAR:   "rune",
	token.IMAG:   "complex128",
}

var builtinTypes = []string{
	"bool", "byte", "complex64", "complex128", "error", "float32", "float64",
	"int", "int8", "int16", "int32", "int64", "rune", "string",
	"uint", "uint8", "uint16", "uint32", "uint64", "uintptr",
}

func isBuiltinType(typ string) bool {
	for _, v := range builtinTypes {
		if v == typ {
			return true
		}
	}
	return false
}

func constTypePriority(typ string) int {
	switch typ {
	case "complex128":
		return 100
	case "ideal-imag":
		return 99
	case "complex64":
		return 98
	case "float64":
		return 97
	case "ideal-float":
		return 96
	case "float32":
		return 95
	case "int64":
		return 94
	case "int", "uint", "uintptr":
		return 93
	case "ideal-int":
		return 92
	case "int16", "uint16", "int8", "uint8", "byte":
		return 91
	case "ideal-char":
		return 90
	}
	return 101
}

func (w *Walker) constRealType(typ string) string {
	pos := strings.Index(typ, ".")
	if pos >= 0 {
		pkg := typ[:pos]
		if pkg == "C" {
			return "int"
		}
		typ = typ[pos+1:]
		if p := w.findPackage(pkg); p != nil {
			ret := p.findType(typ)
			if ret != nil {
				return w.nodeString(w.namelessType(ret))
			}
		}
	} else {
		ret := w.curPackage.findType(typ)
		if ret != nil {
			return w.nodeString(w.namelessType(ret))
		}
	}
	return typ
}

func (w *Walker) constValueType(vi interface{}) (string, error) {
	switch v := vi.(type) {
	case *ast.BasicLit:
		litType, ok := constType[v.Kind]
		if !ok {
			return "", fmt.Errorf("unknown basic literal kind %#v", v)
		}
		return litType, nil
	case *ast.UnaryExpr:
		return w.constValueType(v.X)
	case *ast.SelectorExpr:
		lhs := w.nodeString(v.X)
		rhs := w.nodeString(v.Sel)
		//if CGO
		if lhs == "C" {
			return lhs + "." + rhs, nil
		}
		if p := w.findPackage(lhs); p != nil {
			if ret, ok := p.consts[rhs]; ok {
				return w.pkgRetType(p.name, ret.T), nil
			}
		}
		return "", fmt.Errorf("unknown constant reference to %s.%s", lhs, rhs)
	case *ast.Ident:
		if v.Name == "iota" {
			return "ideal-int", nil // hack.
		}
		if v.Name == "false" || v.Name == "true" {
			return "bool", nil
		}
		if t, ok := w.curPackage.consts[v.Name]; ok {
			return t.T, nil
		}
		return constDepPrefix + v.Name, nil
	case *ast.BinaryExpr:
		//== > < ! != >= <=
		if v.Op == token.EQL || v.Op == token.LSS || v.Op == token.GTR || v.Op == token.NOT ||
			v.Op == token.NEQ || v.Op == token.LEQ || v.Op == token.GEQ {
			return "bool", nil
		}
		left, err := w.constValueType(v.X)
		if err != nil {
			return "", err
		}
		if v.Op == token.SHL || v.Op == token.SHR {
			return left, err
		}
		right, err := w.constValueType(v.Y)
		if err != nil {
			return "", err
		}
		//const left != right , one or two is ideal-
		if left != right {
			if strings.HasPrefix(left, constDepPrefix) && strings.HasPrefix(right, constDepPrefix) {
				// Just pick one.
				// e.g. text/scanner GoTokens const-dependency:ScanIdents, const-dependency:ScanFloats
				return left, nil
			}
			lp := constTypePriority(w.constRealType(left))
			rp := constTypePriority(w.constRealType(right))
			if lp >= rp {
				return left, nil
			} else {
				return right, nil
			}
			return "", fmt.Errorf("in BinaryExpr, unhandled type mismatch; left=%q, right=%q", left, right)
		}
		return left, nil
	case *ast.CallExpr:
		// Not a call, but a type conversion.
		typ := w.nodeString(v.Fun)
		switch typ {
		case "complex":
			return "complex128", nil
		case "real", "imag":
			return "float64", nil
		}
		return typ, nil
	case *ast.ParenExpr:
		return w.constValueType(v.X)
	}
	return "", fmt.Errorf("unknown const value type %T", vi)
}

func (w *Walker) pkgRetType(pkg, ret string) string {
	pkg = pkg[strings.LastIndex(pkg, "/")+1:]
	if strings.HasPrefix(ret, "[]") {
		return "[]" + w.pkgRetType(pkg, ret[2:])
	}
	if strings.HasPrefix(ret, "*") {
		return "*" + w.pkgRetType(pkg, ret[1:])
	}
	if ast.IsExported(ret) {
		return pkg + "." + ret
	}
	return ret
}

func (w *Walker) findStructFieldType(st ast.Expr, name string) ast.Expr {
	_, expr := w.findStructField(st, name)
	return expr
}

func (w *Walker) findStructFieldFunction(st ast.Expr, name string) (*TypeInfo, error) {
	if s, ok := st.(*ast.StructType); ok {
		for _, fi := range s.Fields.List {
			typ := fi.Type
			if fi.Names == nil {
				switch v := typ.(type) {
				case *ast.Ident:
					if t := w.curPackage.findType(v.Name); t != nil {
						return w.lookupFunction(v.Name, name)
					}
				case *ast.SelectorExpr:
					pt := w.nodeString(typ)
					pos := strings.Index(pt, ".")
					if pos != -1 {
						if p := w.findPackage(pt[:pos]); p != nil {
							if t := p.findType(pt[pos+1:]); t != nil {
								return w.lookupFunction(pt, name)
							}
						}
					}
				case *ast.StarExpr:
					return w.findStructFieldFunction(v.X, name)
				default:
					if apiVerbose {
						log.Printf("unable to handle embedded %T", typ)
					}
				}
			}
		}
	}
	return nil, nil
}

func (w *Walker) findStructField(st ast.Expr, name string) (*ast.Ident, ast.Expr) {
	if s, ok := st.(*ast.StructType); ok {
		for _, fi := range s.Fields.List {
			typ := fi.Type
			for _, n := range fi.Names {
				if n.Name == name {
					return n, fi.Type
				}
			}
			if fi.Names == nil {
				switch v := typ.(type) {
				case *ast.Ident:
					if t := w.curPackage.findType(v.Name); t != nil {
						if v.Name == name {
							return v, v
						}
						id, expr := w.findStructField(t, name)
						if id != nil {
							return id, expr
						}
					}
				case *ast.StarExpr:
					switch vv := v.X.(type) {
					case *ast.Ident:
						if t := w.curPackage.findType(vv.Name); t != nil {
							if vv.Name == name {
								return vv, v.X
							}
							id, expr := w.findStructField(t, name)
							if id != nil {
								return id, expr
							}
						}
					case *ast.SelectorExpr:
						pt := w.nodeString(typ)
						pos := strings.Index(pt, ".")
						if pos != -1 {
							if p := w.findPackage(pt[:pos]); p != nil {
								if t := p.findType(pt[pos+1:]); t != nil {
									return w.findStructField(t, name)
								}
							}
						}
					default:
						if apiVerbose {
							log.Printf("unable to handle embedded starexpr before %T", typ)
						}
					}
				case *ast.SelectorExpr:
					pt := w.nodeString(typ)
					pos := strings.Index(pt, ".")
					if pos != -1 {
						if p := w.findPackage(pt[:pos]); p != nil {
							if t := p.findType(pt[pos+1:]); t != nil {
								return w.findStructField(t, name)
							}
						}
					}
				default:
					if apiVerbose {
						log.Printf("unable to handle embedded %T", typ)
					}
				}
			}
		}
	}
	return nil, nil
}

func (w *Walker) lookupFunction(name, sel string) (*TypeInfo, error) {
	name = strings.TrimLeft(name, "*")
	if p := w.findPackage(name); p != nil {
		fn := p.findCallFunc(sel)
		if fn != nil {
			return &TypeInfo{Kind: KindFunc, X: fn, Name: name + "." + sel, T: fn, Type: w.nodeString(w.namelessType(fn))}, nil
		}
	}
	pos := strings.Index(name, ".")
	if pos != -1 {
		pkg := name[:pos]
		typ := name[pos+1:]
		if p := w.findPackage(pkg); p != nil {
			if ident, fn := p.findMethod(typ, sel); fn != nil {
				return &TypeInfo{Kind: KindMethod, X: fn, Name: name + "." + sel, T: ident, Type: w.nodeString(w.namelessType(fn))}, nil
			}
		}
		return nil, fmt.Errorf("not lookup pkg type function pkg: %s, %s. %s. %s", name, pkg, typ, sel)
	}

	//find local var.func()
	if ns, nt, n := w.resolveName(name); n >= 0 {
		var vt string
		if nt != nil {
			vt = w.nodeString(w.namelessType(nt))
		} else if ns != nil {
			typ, err := w.varValueType(ns, n)
			if err == nil {
				vt = typ
			}
		} else {
			typ := w.curPackage.findSelectorType(name)
			if typ != nil {
				vt = w.nodeString(w.namelessType(typ))
			}
		}
		if strings.HasPrefix(vt, "*") {
			vt = vt[1:]
		}
		if vt == "error" && sel == "Error" {
			return &TypeInfo{Kind: KindBuiltin, Name: "error.Error", Type: "()string"}, nil
		}
		if fn, ok := w.curPackage.functions[vt+"."+sel]; ok {
			return &TypeInfo{Kind: KindMethod, X: fn.ft, Name: name + "." + sel, T: fn.ft, Type: w.nodeString(w.namelessType(fn))}, nil
		}
	}
	if typ, ok := w.curPackage.structs[name]; ok {
		if fn, ok := w.curPackage.functions[name+"."+sel]; ok {
			return &TypeInfo{Kind: KindMethod, X: fn.ft, Name: name + "." + sel, T: fn.ft, Type: w.nodeString(w.namelessType(fn.ft))}, nil
		}
		if info, err := w.findStructFieldFunction(typ, sel); err == nil {
			return info, nil
		}
		// struct field is type function
		if ft := w.findStructFieldType(typ, sel); ft != nil {
			typ, err := w.varValueType(ft, 0)
			if err != nil {
				typ = w.nodeString(ft)
			}
			return &TypeInfo{Kind: KindField, X: ft, Name: name + "." + sel, T: ft, Type: typ}, nil
		}
	}

	if ident, fn := w.curPackage.findMethod(name, sel); ident != nil && fn != nil {
		return &TypeInfo{Kind: KindMethod, X: fn, Name: name + "." + sel, T: ident, Type: w.nodeString(w.namelessType(fn))}, nil
	}

	if p := w.findPackage(name); p != nil {
		fn := p.findCallFunc(sel)
		if fn != nil {
			return &TypeInfo{Kind: KindFunc, X: fn, Name: name + "." + sel, T: fn, Type: w.nodeString(w.namelessType(fn))}, nil
		}
		return nil, fmt.Errorf("not find pkg func0 %v.%v", p.name, sel)
	}
	return nil, fmt.Errorf("not lookup func %v.%v", name, sel)
}

func (w *Walker) varFunctionType(name, sel string, index int) (string, error) {
	name = strings.TrimLeft(name, "*")
	pos := strings.Index(name, ".")
	if pos != -1 {
		pkg := name[:pos]
		typ := name[pos+1:]

		if p := w.findPackage(pkg); p != nil {
			_, fn := p.findMethod(typ, sel)
			if fn != nil {
				ret := funcRetType(fn, index)
				if ret != nil {
					return w.pkgRetType(p.name, w.nodeString(w.namelessType(ret))), nil
				}
			}
		}
		return "", fmt.Errorf("unknown pkg type function pkg: %s.%s.%s", pkg, typ, sel)
	}
	//find local var
	if v, ok := w.localvar[name]; ok {
		vt := v.T
		if strings.HasPrefix(vt, "*") {
			vt = vt[1:]
		}
		if vt == "error" && sel == "Error" {
			return "string", nil
		}
		typ, err := w.varFunctionType(vt, sel, 0)
		if err == nil {
			return typ, nil
		}
	}
	//find global var.func()
	if ns, nt, n := w.resolveName(name); n >= 0 {
		var vt string
		if nt != nil {
			vt = w.nodeString(w.namelessType(nt))
		} else if ns != nil {
			typ, err := w.varValueType(ns, n)
			if err == nil {
				vt = typ
			}
		} else {
			typ := w.curPackage.findSelectorType(name)
			if typ != nil {
				vt = w.nodeString(w.namelessType(typ))
			}
		}
		if strings.HasPrefix(vt, "*") {
			vt = vt[1:]
		}
		if vt == "error" && sel == "Error" {
			return "string", nil
		}
		if fn, ok := w.curPackage.functions[vt+"."+sel]; ok {
			return w.nodeString(w.namelessType(funcRetType(fn.ft, index))), nil
		}
	}
	if typ, ok := w.curPackage.structs[name]; ok {
		if ft := w.findStructFieldType(typ, sel); ft != nil {
			return w.varValueType(ft, index)
		}
	}
	//find pkg.func()
	if p := w.findPackage(name); p != nil {
		typ := p.findCallType(sel, index)
		if typ != nil {
			return w.pkgRetType(p.name, w.nodeString(w.namelessType(typ))), nil
		}
		//log.Println("->", p.functions)
		return "", fmt.Errorf("not find pkg func1 %v . %v", p.name, sel)
	}
	return "", fmt.Errorf("not find func %v.%v", name, sel)
}

func (w *Walker) lookupSelector(name string, sel string) (*TypeInfo, error) {
	name = strings.TrimLeft(name, "*")
	pos := strings.Index(name, ".")
	if pos != -1 {
		pkg := name[:pos]
		typ := name[pos+1:]
		if p := w.findPackage(pkg); p != nil {
			t := p.findType(typ)
			if t != nil {
				typ := w.findStructFieldType(t, sel)
				if typ != nil {
					return &TypeInfo{Kind: KindField, X: typ, Name: name + "." + sel, T: typ, Type: w.pkgRetType(p.name, w.nodeString(w.namelessType(typ)))}, nil
				}
			}
		}
		return nil, fmt.Errorf("lookup unknown pkg type selector pkg: %s.%s %s", pkg, typ, sel)
	}

	if lv, ok := w.localvar[name]; ok {
		return w.lookupSelector(lv.T, sel)
	}

	vs, vt, n := w.resolveName(name)
	if n >= 0 {
		var typ string
		if vt != nil {
			typ = w.nodeString(w.namelessType(vt))
		} else {
			typ, _ = w.varValueType(vs, n)
		}
		if strings.HasPrefix(typ, "*") {
			typ = typ[1:]
		}
		//typ is type, find real type
		for k, v := range w.curPackage.types {
			if k == typ {
				typ = w.nodeString(w.namelessType(v))
			}
		}
		pos := strings.Index(typ, ".")
		if pos == -1 {
			t := w.curPackage.findType(typ)
			if t != nil {
				typ := w.findStructFieldType(t, sel)
				if typ != nil {
					return &TypeInfo{Kind: KindField, X: typ, Name: name + "." + sel, T: typ, Type: w.nodeString(w.namelessType(typ))}, nil
				}
			}
		} else {
			name := typ[:pos]
			typ = typ[pos+1:]
			if p := w.findPackage(name); p != nil {
				t := p.findType(typ)
				if t != nil {
					typ := w.findStructFieldType(t, sel)
					if typ != nil {
						return &TypeInfo{Kind: KindField, X: typ, Name: name + "." + sel, T: typ, Type: w.nodeString(w.namelessType(typ))}, nil
					}
				}
			}
		}
	}
	if p := w.findPackage(name); p != nil {
		typ := p.findSelectorType(sel)
		if typ != nil {
			return &TypeInfo{Kind: KindType, X: typ, Name: name + "." + sel, T: typ, Type: w.pkgRetType(p.name, w.nodeString(w.namelessType(typ)))}, nil
		}
	}
	t := w.curPackage.findType(name)
	if t != nil {
		typ := w.findStructFieldType(t, sel)
		if typ != nil {
			return &TypeInfo{Kind: KindField, X: typ, Name: name + "." + sel, T: typ, Type: w.nodeString(w.namelessType(typ))}, nil
		}
	}
	if t, ok := w.curPackage.types[name]; ok {
		return w.lookupSelector(w.nodeString(t), sel)
	}
	return nil, fmt.Errorf("unknown selector expr ident: %s.%s", name, sel)
}

func (w *Walker) varSelectorType(name string, sel string) (string, error) {
	name = strings.TrimLeft(name, "*")
	pos := strings.Index(name, ".")
	if pos != -1 {
		pkg := name[:pos]
		typ := name[pos+1:]
		if p := w.findPackage(pkg); p != nil {
			t := p.findType(typ)
			if t != nil {
				typ := w.findStructFieldType(t, sel)
				if typ != nil {
					return w.pkgRetType(pkg, w.nodeString(w.namelessType(typ))), nil
				}
			}
		}
		return "", fmt.Errorf("unknown pkg type selector pkg: %s.%s.%s", pkg, typ, sel)
	}
	//check local
	if lv, ok := w.localvar[name]; ok {
		return w.varSelectorType(lv.T, sel)
	}
	//check struct
	if t := w.curPackage.findType(name); t != nil {
		typ := w.findStructFieldType(t, sel)
		if typ != nil {
			return w.nodeString(w.namelessType(typ)), nil
		}
	}
	//check var
	vs, vt, n := w.resolveName(name)
	if n >= 0 {
		var typ string
		if vt != nil {
			typ = w.nodeString(w.namelessType(vt))
		} else {
			typ, _ = w.varValueType(vs, n)
		}
		if strings.HasPrefix(typ, "*") {
			typ = typ[1:]
		}
		//typ is type, find real type
		for k, v := range w.curPackage.types {
			if k == typ {
				typ = w.nodeString(w.namelessType(v))
			}
		}
		pos := strings.Index(typ, ".")
		if pos == -1 {
			t := w.curPackage.findType(typ)
			if t != nil {
				typ := w.findStructFieldType(t, sel)
				if typ != nil {
					return w.nodeString(w.namelessType(typ)), nil
				}
			}
		} else {
			name := typ[:pos]
			typ = typ[pos+1:]
			if p := w.findPackage(name); p != nil {
				t := p.findType(typ)
				if t != nil {
					typ := w.findStructFieldType(t, sel)
					if typ != nil {
						return w.nodeString(w.namelessType(typ)), nil
					}
				}
			}
		}
	}

	if p := w.findPackage(name); p != nil {
		typ := p.findSelectorType(sel)
		if typ != nil {
			return w.pkgRetType(p.name, w.nodeString(w.namelessType(typ))), nil
		}
	}
	return "", fmt.Errorf("unknown var selector expr ident: %s.%s", name, sel)
}

func (w *Walker) varValueType(vi ast.Expr, index int) (string, error) {
	if vi == nil {
		return "", nil
	}
	switch v := vi.(type) {
	case *ast.BasicLit:
		litType, ok := varType[v.Kind]
		if !ok {
			return "", fmt.Errorf("unknown basic literal kind %#v", v)
		}
		return litType, nil
	case *ast.CompositeLit:
		return w.nodeString(v.Type), nil
	case *ast.FuncLit:
		return w.nodeString(w.namelessType(v.Type)), nil
	case *ast.InterfaceType:
		return w.nodeString(v), nil
	case *ast.Ellipsis:
		typ, err := w.varValueType(v.Elt, index)
		if err != nil {
			return "", err
		}
		return "[]" + typ, nil
	case *ast.StarExpr:
		typ, err := w.varValueType(v.X, index)
		if err != nil {
			return "", err
		}
		return "*" + typ, err
	case *ast.UnaryExpr:
		if v.Op == token.AND {
			typ, err := w.varValueType(v.X, index)
			return "*" + typ, err
		}
		return "", fmt.Errorf("unknown unary expr: %#v", v)
	case *ast.SelectorExpr:
		switch st := v.X.(type) {
		case *ast.Ident:
			return w.varSelectorType(st.Name, v.Sel.Name)
		case *ast.CallExpr:
			typ, err := w.varValueType(v.X, index)
			if err == nil {
				if strings.HasPrefix(typ, "*") {
					typ = typ[1:]
				}
				t := w.curPackage.findType(typ)
				if st, ok := t.(*ast.StructType); ok {
					for _, fi := range st.Fields.List {
						for _, n := range fi.Names {
							if n.Name == v.Sel.Name {
								return w.varValueType(fi.Type, index)
							}
						}
					}
				}
			}
		case *ast.SelectorExpr:
			typ, err := w.varValueType(v.X, index)
			if err == nil {
				return w.varSelectorType(typ, v.Sel.Name)
			}
		case *ast.IndexExpr:
			typ, err := w.varValueType(st.X, index)
			if err == nil {
				if strings.HasPrefix(typ, "[]") {
					return w.varSelectorType(typ[2:], v.Sel.Name)
				}
			}
		case *ast.CompositeLit:
			typ, err := w.varValueType(st.Type, 0)
			if err == nil {
				//log.Println(typ, v.Sel.Name)
				t, err := w.varSelectorType(typ, v.Sel.Name)
				if err == nil {
					return t, nil
				}
			}
		}
		return "", fmt.Errorf("var unknown selector expr: %T %s.%s", v.X, w.nodeString(v.X), v.Sel)
	case *ast.Ident:
		if v.Name == "true" || v.Name == "false" {
			return "bool", nil
		}
		if isBuiltinType(v.Name) {
			return v.Name, nil
		}
		if lv, ok := w.localvar[v.Name]; ok {
			return lv.T, nil
		}
		vt := w.curPackage.findType(v.Name)
		if vt != nil {
			if _, ok := vt.(*ast.StructType); ok {
				return v.Name, nil
			}
			return w.nodeString(vt), nil
		}
		vs, _, n := w.resolveName(v.Name)
		if n >= 0 {
			return w.varValueType(vs, n)
		}
		return "", fmt.Errorf("unresolved identifier: %q", v.Name)
	case *ast.BinaryExpr:
		//== > < ! != >= <=
		if v.Op == token.EQL || v.Op == token.LSS || v.Op == token.GTR || v.Op == token.NOT ||
			v.Op == token.NEQ || v.Op == token.LEQ || v.Op == token.GEQ {
			return "bool", nil
		}
		left, err := w.varValueType(v.X, index)
		if err != nil {
			return "", err
		}
		right, err := w.varValueType(v.Y, index)
		if err != nil {
			return "", err
		}
		if left != right {
			return "", fmt.Errorf("in BinaryExpr, unhandled type mismatch; left=%q, right=%q", left, right)
		}
		return left, nil
	case *ast.ParenExpr:
		return w.varValueType(v.X, index)
	case *ast.CallExpr:
		switch ft := v.Fun.(type) {
		case *ast.ArrayType:
			return w.nodeString(v.Fun), nil
		case *ast.Ident:
			switch ft.Name {
			case "make":
				return w.nodeString(w.namelessType(v.Args[0])), nil
			case "new":
				return "*" + w.nodeString(w.namelessType(v.Args[0])), nil
			case "append":
				return w.varValueType(v.Args[0], 0)
			case "recover":
				return "interface{}", nil
			case "len", "cap", "copy":
				return "int", nil
			case "complex":
				return "complex128", nil
			case "real":
				return "float64", nil
			case "imag":
				return "float64", nil
			}
			if isBuiltinType(ft.Name) {
				return ft.Name, nil
			}
			typ := w.curPackage.findCallType(ft.Name, index)
			if typ != nil {
				return w.nodeString(w.namelessType(typ)), nil
			}
			//if local var type
			if fn, ok := w.localvar[ft.Name]; ok {
				typ := fn.T
				if strings.HasPrefix(typ, "func(") {
					expr, err := parser.ParseExpr(typ + "{}")
					if err == nil {
						if fl, ok := expr.(*ast.FuncLit); ok {
							retType := funcRetType(fl.Type, index)
							if retType != nil {
								return w.nodeString(w.namelessType(retType)), nil
							}
						}
					}
				}
			}
			//if var is func() type
			vs, _, n := w.resolveName(ft.Name)
			if n >= 0 {
				if vs != nil {
					typ, err := w.varValueType(vs, n)
					if err == nil {
						if strings.HasPrefix(typ, "func(") {
							expr, err := parser.ParseExpr(typ + "{}")
							if err == nil {
								if fl, ok := expr.(*ast.FuncLit); ok {
									retType := funcRetType(fl.Type, index)
									if retType != nil {
										return w.nodeString(w.namelessType(retType)), nil
									}
								}
							}
						}
					}
				}
			}
			return "", fmt.Errorf("unknown funcion %s %s", w.curPackageName, ft.Name)
		case *ast.SelectorExpr:
			typ, err := w.varValueType(ft.X, index)
			if err == nil {
				if strings.HasPrefix(typ, "*") {
					typ = typ[1:]
				}
				retType := w.curPackage.findCallType(typ+"."+ft.Sel.Name, index)
				if retType != nil {
					return w.nodeString(w.namelessType(retType)), nil
				}
			}
			switch st := ft.X.(type) {
			case *ast.Ident:
				return w.varFunctionType(st.Name, ft.Sel.Name, index)
			case *ast.CallExpr:
				typ, err := w.varValueType(st, 0)
				if err != nil {
					return "", err
				}
				return w.varFunctionType(typ, ft.Sel.Name, index)
			case *ast.SelectorExpr:
				typ, err := w.varValueType(st, index)
				if err == nil {
					return w.varFunctionType(typ, ft.Sel.Name, index)
				}
			case *ast.IndexExpr:
				typ, err := w.varValueType(st.X, index)
				if err == nil {
					if strings.HasPrefix(typ, "[]") {
						return w.varFunctionType(typ[2:], ft.Sel.Name, index)
					}
				}
			case *ast.TypeAssertExpr:
				typ := w.nodeString(w.namelessType(st.Type))
				typ = strings.TrimLeft(typ, "*")
				return w.varFunctionType(typ, ft.Sel.Name, index)
			}
			return "", fmt.Errorf("unknown var function selector %v %T", w.nodeString(ft.X), ft.X)
		case *ast.FuncLit:
			retType := funcRetType(ft.Type, index)
			if retType != nil {
				return w.nodeString(w.namelessType(retType)), nil
			}
		case *ast.CallExpr:
			typ, err := w.varValueType(v.Fun, 0)
			if err == nil && strings.HasPrefix(typ, "func(") {
				expr, err := parser.ParseExpr(typ + "{}")
				if err == nil {
					if fl, ok := expr.(*ast.FuncLit); ok {
						retType := funcRetType(fl.Type, index)
						if retType != nil {
							return w.nodeString(w.namelessType(retType)), nil
						}
					}
				}
			}
		}
		return "", fmt.Errorf("not a known function %T %v", v.Fun, w.nodeString(v.Fun))
	case *ast.MapType:
		return fmt.Sprintf("map[%s](%s)", w.nodeString(w.namelessType(v.Key)), w.nodeString(w.namelessType(v.Value))), nil
	case *ast.ArrayType:
		return fmt.Sprintf("[]%s", w.nodeString(w.namelessType(v.Elt))), nil
	case *ast.FuncType:
		return w.nodeString(w.namelessType(v)), nil
	case *ast.IndexExpr:
		typ, err := w.varValueType(v.X, index)
		typ = strings.TrimLeft(typ, "*")
		if err == nil {
			if index == 0 {
				return typ, nil
			} else if index == 1 {
				return "bool", nil
			}
			if strings.HasPrefix(typ, "[]") {
				return typ[2:], nil
			} else if strings.HasPrefix(typ, "map[") {
				node, err := parser.ParseExpr(typ + "{}")
				if err == nil {
					if cl, ok := node.(*ast.CompositeLit); ok {
						if m, ok := cl.Type.(*ast.MapType); ok {
							return w.nodeString(w.namelessType(m.Value)), nil
						}
					}
				}
			}
		}
		return "", fmt.Errorf("unknown index %v %v %v %v", typ, v.X, index, err)
	case *ast.SliceExpr:
		return w.varValueType(v.X, index)
	case *ast.ChanType:
		typ, err := w.varValueType(v.Value, index)
		if err == nil {
			if v.Dir == ast.RECV {
				return "<-chan " + typ, nil
			} else if v.Dir == ast.SEND {
				return "chan<- " + typ, nil
			}
			return "chan " + typ, nil
		}
	case *ast.TypeAssertExpr:
		if index == 1 {
			return "bool", nil
		}
		return w.nodeString(w.namelessType(v.Type)), nil
	default:
		return "", fmt.Errorf("unknown value type %v %T", w.nodeString(vi), vi)
	}
	//panic("unreachable")
	return "", fmt.Errorf("unreachable value type %v %T", vi, vi)
}

// resolveName finds a top-level node named name and returns the node
// v and its type t, if known.
func (w *Walker) resolveName(name string) (v ast.Expr, t interface{}, n int) {
	for _, file := range w.curPackage.apkg.Files {
		for _, di := range file.Decls {
			switch d := di.(type) {
			case *ast.GenDecl:
				switch d.Tok {
				case token.VAR:
					for _, sp := range d.Specs {
						vs := sp.(*ast.ValueSpec)
						for i, vname := range vs.Names {
							if vname.Name == name {
								if len(vs.Values) == 1 {
									return vs.Values[0], vs.Type, i
								}
								return nil, vs.Type, i
							}
						}
					}
				}
			}
		}
	}
	return nil, nil, -1
}

// constDepPrefix is a magic prefix that is used by constValueType
// and walkConst to signal that a type isn't known yet. These are
// resolved at the end of walking of a package's files.
const constDepPrefix = "const-dependency:"

func (w *Walker) walkConst(vs *ast.ValueSpec) {
	for _, ident := range vs.Names {
		if !w.isExtract(ident.Name) {
			continue
		}
		litType := ""
		if vs.Type != nil {
			litType = w.nodeString(vs.Type)
		} else {
			litType = w.lastConstType
			if vs.Values != nil {
				if len(vs.Values) != 1 {
					log.Fatalf("const %q, values: %#v", ident.Name, vs.Values)
				}
				var err error
				litType, err = w.constValueType(vs.Values[0])
				if err != nil {
					if apiVerbose {
						log.Printf("unknown kind in const %q (%T): %v", ident.Name, vs.Values[0], err)
					}
					litType = "unknown-type"
				}
			}
		}
		if strings.HasPrefix(litType, constDepPrefix) {
			dep := litType[len(constDepPrefix):]
			w.constDep[ident.Name] = &ExprType{T: dep, X: ident}
			continue
		}
		if litType == "" {
			if apiVerbose {
				log.Printf("unknown kind in const %q", ident.Name)
			}
			continue
		}
		w.lastConstType = litType

		w.curPackage.consts[ident.Name] = &ExprType{T: litType, X: ident}

		if isExtract(ident.Name) {
			w.emitFeature(fmt.Sprintf("const %s %s", ident, litType), ident.Pos())
		}
	}
}

func (w *Walker) resolveConstantDeps() {
	var findConstType func(string) string
	findConstType = func(ident string) string {
		if dep, ok := w.constDep[ident]; ok {
			return findConstType(dep.T)
		}
		if t, ok := w.curPackage.consts[ident]; ok {
			return t.T
		}
		return ""
	}
	for ident, info := range w.constDep {
		if !isExtract(ident) {
			continue
		}
		t := findConstType(ident)
		if t == "" {
			if apiVerbose {
				log.Printf("failed to resolve constant %q", ident)
			}
			continue
		}
		w.curPackage.consts[ident] = &ExprType{T: t, X: info.X}
		w.emitFeature(fmt.Sprintf("const %s %s", ident, t), info.X.Pos())
	}
}

func (w *Walker) walkVar(vs *ast.ValueSpec) {
	if vs.Type != nil {
		typ := w.nodeString(vs.Type)
		for _, ident := range vs.Names {
			w.curPackage.vars[ident.Name] = &ExprType{T: typ, X: ident}
			if isExtract(ident.Name) {
				w.emitFeature(fmt.Sprintf("var %s %s", ident, typ), ident.Pos())
			}
		}
	} else if len(vs.Names) == len(vs.Values) {
		for n, ident := range vs.Names {
			if !w.isExtract(ident.Name) {
				continue
			}
			typ, err := w.varValueType(vs.Values[n], n)
			if err != nil {
				if apiVerbose {
					log.Printf("unknown type of variable0 %q, type %T, error = %v, pos=%s",
						ident.Name, vs.Values[n], err, w.fset.Position(vs.Pos()))
				}
				typ = "unknown-type"
			}
			w.curPackage.vars[ident.Name] = &ExprType{T: typ, X: ident}
			if isExtract(ident.Name) {
				w.emitFeature(fmt.Sprintf("var %s %s", ident, typ), ident.Pos())
			}
		}
	} else if len(vs.Values) == 1 {
		for n, ident := range vs.Names {
			if !w.isExtract(ident.Name) {
				continue
			}
			typ, err := w.varValueType(vs.Values[0], n)
			if err != nil {
				if apiVerbose {
					log.Printf("unknown type of variable1 %q, type %T, error = %v, pos=%s",
						ident.Name, vs.Values[0], err, w.fset.Position(vs.Pos()))
				}
				typ = "unknown-type"
			}
			w.curPackage.vars[ident.Name] = &ExprType{T: typ, X: ident}
			if isExtract(ident.Name) {
				w.emitFeature(fmt.Sprintf("var %s %s", ident, typ), ident.Pos())
			}
		}
	}
}

func (w *Walker) nodeString(node interface{}) string {
	if node == nil {
		return ""
	}
	var b bytes.Buffer
	printer.Fprint(&b, w.fset, node)
	return b.String()
}

func (w *Walker) nodeDebug(node interface{}) string {
	if node == nil {
		return ""
	}
	var b bytes.Buffer
	ast.Fprint(&b, w.fset, node, nil)
	return b.String()
}

func (w *Walker) noteInterface(name string, it *ast.InterfaceType) {
	w.interfaces[pkgSymbol{w.curPackageName, name}] = it
}

func (w *Walker) walkTypeSpec(ts *ast.TypeSpec) {
	name := ts.Name.Name
	if !isExtract(name) {
		return
	}
	switch t := ts.Type.(type) {
	case *ast.StructType:
		w.walkStructType(name, t)
	case *ast.InterfaceType:
		w.walkInterfaceType(name, t)
	default:
		w.emitFeature(fmt.Sprintf("type %s %s", name, w.nodeString(ts.Type)), t.Pos()-token.Pos(len(name)+1))
	}
}

func (w *Walker) walkStructType(name string, t *ast.StructType) {
	typeStruct := fmt.Sprintf("type %s struct", name)
	w.emitFeature(typeStruct, t.Pos()-token.Pos(len(name)+1))
	pop := w.pushScope(typeStruct)
	defer pop()
	for _, f := range t.Fields.List {
		typ := f.Type
		for _, name := range f.Names {
			if isExtract(name.Name) {
				w.emitFeature(fmt.Sprintf("%s %s", name, w.nodeString(w.namelessType(typ))), name.Pos())
			}
		}
		if f.Names == nil {
			switch v := typ.(type) {
			case *ast.Ident:
				if isExtract(v.Name) {
					w.emitFeature(fmt.Sprintf("embedded %s", v.Name), v.Pos())
				}
			case *ast.StarExpr:
				switch vv := v.X.(type) {
				case *ast.Ident:
					if isExtract(vv.Name) {
						w.emitFeature(fmt.Sprintf("embedded *%s", vv.Name), vv.Pos())
					}
				case *ast.SelectorExpr:
					w.emitFeature(fmt.Sprintf("embedded %s", w.nodeString(typ)), v.Pos())
				default:
					log.Fatalf("unable to handle embedded starexpr before %T", typ)
				}
			case *ast.SelectorExpr:
				w.emitFeature(fmt.Sprintf("embedded %s", w.nodeString(typ)), v.Pos())
			default:
				if apiVerbose {
					log.Printf("unable to handle embedded %T", typ)
				}
			}
		}
	}
}

// typeMethod is a method of an interface.
type typeMethod struct {
	name string // "Read"
	sig  string // "([]byte) (int, error)", from funcSigString
	ft   *ast.FuncType
	pos  token.Pos
	recv ast.Expr
}

// interfaceMethods returns the expanded list of exported methods for an interface.
// The boolean complete reports whether the list contains all methods (that is, the
// interface has no unexported methods).
// pkg is the complete package name ("net/http")
// iname is the interface name.
func (w *Walker) interfaceMethods(pkg, iname string) (methods []typeMethod, complete bool) {
	t, ok := w.interfaces[pkgSymbol{pkg, iname}]
	if !ok {
		if apiVerbose {
			log.Printf("failed to find interface %s.%s", pkg, iname)
		}
		return
	}

	complete = true
	for _, f := range t.Methods.List {
		typ := f.Type
		switch tv := typ.(type) {
		case *ast.FuncType:
			for _, mname := range f.Names {
				if isExtract(mname.Name) {
					ft := typ.(*ast.FuncType)
					methods = append(methods, typeMethod{
						name: mname.Name,
						sig:  w.funcSigString(ft),
						ft:   ft,
						pos:  f.Pos(),
					})
				} else {
					complete = false
				}
			}
		case *ast.Ident:
			embedded := typ.(*ast.Ident).Name
			if embedded == "error" {
				methods = append(methods, typeMethod{
					name: "Error",
					sig:  "() string",
					ft: &ast.FuncType{
						Params: nil,
						Results: &ast.FieldList{
							List: []*ast.Field{
								&ast.Field{
									Type: &ast.Ident{
										Name: "string",
									},
								},
							},
						},
					},
					pos: f.Pos(),
				})
				continue
			}
			if !isExtract(embedded) {
				log.Fatalf("unexported embedded interface %q in exported interface %s.%s; confused",
					embedded, pkg, iname)
			}
			m, c := w.interfaceMethods(pkg, embedded)
			methods = append(methods, m...)
			complete = complete && c
		case *ast.SelectorExpr:
			lhs := w.nodeString(tv.X)
			rhs := w.nodeString(tv.Sel)
			fpkg, ok := w.selectorFullPkg[lhs]
			if !ok {
				log.Fatalf("can't resolve selector %q in interface %s.%s", lhs, pkg, iname)
			}
			m, c := w.interfaceMethods(fpkg, rhs)
			methods = append(methods, m...)
			complete = complete && c
		default:
			log.Fatalf("unknown type %T in interface field", typ)
		}
	}
	return
}

func (w *Walker) walkInterfaceType(name string, t *ast.InterfaceType) {
	methNames := []string{}
	pop := w.pushScope("type " + name + " interface")
	methods, complete := w.interfaceMethods(w.curPackageName, name)
	w.packageMap[w.curPackageName].interfaceMethods[name] = methods
	for _, m := range methods {
		methNames = append(methNames, m.name)
		w.emitFeature(fmt.Sprintf("%s%s", m.name, m.sig), m.pos)
	}
	if !complete {
		// The method set has unexported methods, so all the
		// implementations are provided by the same package,
		// so the method set can be extended. Instead of recording
		// the full set of names (below), record only that there were
		// unexported methods. (If the interface shrinks, we will notice
		// because a method signature emitted during the last loop,
		// will disappear.)
		w.emitFeature("unexported methods", 0)
	}
	pop()

	if !complete {
		return
	}

	sort.Strings(methNames)
	if len(methNames) == 0 {
		w.emitFeature(fmt.Sprintf("type %s interface {}", name), t.Pos()-token.Pos(len(name)+1))
	} else {
		w.emitFeature(fmt.Sprintf("type %s interface { %s }", name, strings.Join(methNames, ", ")), t.Pos()-token.Pos(len(name)+1))
	}
}

func baseTypeName(x ast.Expr) (name string, imported bool) {
	switch t := x.(type) {
	case *ast.Ident:
		return t.Name, false
	case *ast.SelectorExpr:
		if _, ok := t.X.(*ast.Ident); ok {
			// only possible for qualified type names;
			// assume type is imported
			return t.Sel.Name, true
		}
	case *ast.StarExpr:
		return baseTypeName(t.X)
	}
	return
}

func (w *Walker) peekFuncDecl(f *ast.FuncDecl) {
	var fname = f.Name.Name
	var recv ast.Expr
	if f.Recv != nil {
		recvTypeName, imp := baseTypeName(f.Recv.List[0].Type)
		if imp {
			return
		}
		fname = recvTypeName + "." + f.Name.Name
		recv = f.Recv.List[0].Type
	}
	// Record return type for later use.
	//if f.Type.Results != nil && len(f.Type.Results.List) >= 1 {
	// record all function
	w.curPackage.functions[fname] = typeMethod{
		name: fname,
		sig:  w.funcSigString(f.Type),
		ft:   f.Type,
		pos:  f.Pos(),
		recv: recv,
	}
	//}
}

func (w *Walker) walkFuncDecl(f *ast.FuncDecl) {
	if !w.isExtract(f.Name.Name) {
		return
	}
	if f.Recv != nil {
		// Method.
		recvType := w.nodeString(f.Recv.List[0].Type)
		keep := isExtract(recvType) ||
			(strings.HasPrefix(recvType, "*") &&
				isExtract(recvType[1:]))
		if !keep {
			return
		}
		w.emitFeature(fmt.Sprintf("method (%s) %s%s", recvType, f.Name.Name, w.funcSigString(f.Type)), f.Name.Pos())
		return
	}
	// Else, a function
	w.emitFeature(fmt.Sprintf("func %s%s", f.Name.Name, w.funcSigString(f.Type)), f.Name.Pos())
}

func (w *Walker) funcSigString(ft *ast.FuncType) string {
	var b bytes.Buffer
	writeField := func(b *bytes.Buffer, f *ast.Field) {
		if n := len(f.Names); n > 1 {
			for i := 0; i < n; i++ {
				if i > 0 {
					b.WriteString(", ")
				}
				b.WriteString(w.nodeString(w.namelessType(f.Type)))
			}
		} else {
			b.WriteString(w.nodeString(w.namelessType(f.Type)))
		}
	}
	b.WriteByte('(')
	if ft.Params != nil {
		for i, f := range ft.Params.List {
			if i > 0 {
				b.WriteString(", ")
			}
			writeField(&b, f)
		}
	}
	b.WriteByte(')')
	if ft.Results != nil {
		nr := 0
		for _, f := range ft.Results.List {
			if n := len(f.Names); n > 1 {
				nr += n
			} else {
				nr++
			}
		}
		if nr > 0 {
			b.WriteByte(' ')
			if nr > 1 {
				b.WriteByte('(')
			}
			for i, f := range ft.Results.List {
				if i > 0 {
					b.WriteString(", ")
				}
				writeField(&b, f)
			}
			if nr > 1 {
				b.WriteByte(')')
			}
		}
	}
	return b.String()
}

// namelessType returns a type node that lacks any variable names.
func (w *Walker) namelessType(t interface{}) interface{} {
	ft, ok := t.(*ast.FuncType)
	if !ok {
		return t
	}
	return &ast.FuncType{
		Params:  w.namelessFieldList(ft.Params),
		Results: w.namelessFieldList(ft.Results),
	}
}

// namelessFieldList returns a deep clone of fl, with the cloned fields
// lacking names.
func (w *Walker) namelessFieldList(fl *ast.FieldList) *ast.FieldList {
	fl2 := &ast.FieldList{}
	if fl != nil {
		for _, f := range fl.List {
			n := len(f.Names)
			if n >= 1 {
				for i := 0; i < n; i++ {
					fl2.List = append(fl2.List, w.namelessField(f))
				}
			} else {
				fl2.List = append(fl2.List, w.namelessField(f))
			}
		}
	}
	return fl2
}

// namelessField clones f, but not preserving the names of fields.
// (comments and tags are also ignored)
func (w *Walker) namelessField(f *ast.Field) *ast.Field {
	return &ast.Field{
		Type: f.Type,
	}
}

func (w *Walker) emitFeature(feature string, pos token.Pos) {
	if !w.wantedPkg[w.curPackage.name] {
		return
	}
	more := strings.Index(feature, "\n")
	if more != -1 {
		if len(feature) <= 1024 {
			feature = strings.Replace(feature, "\n", " ", 1)
			feature = strings.Replace(feature, "\n", ";", -1)
			feature = strings.Replace(feature, "\t", " ", -1)
		} else {
			feature = feature[:more] + " ...more"
			if apiVerbose {
				log.Printf("feature contains newlines: %v, %s", feature, w.fset.Position(pos))
			}
		}
	}
	f := strings.Join(w.scope, w.sep) + w.sep + feature

	if _, dup := w.curPackage.features[f]; dup {
		return
	}
	w.curPackage.features[f] = pos
}

func strListContains(l []string, s string) bool {
	for _, v := range l {
		if v == s {
			return true
		}
	}
	return false
}

const goosList = "darwin freebsd linux netbsd openbsd plan9 windows "
const goarchList = "386 amd64 arm "

// goodOSArchFile returns false if the name contains a $GOOS or $GOARCH
// suffix which does not match the current system.
// The recognized name formats are:
//
//     name_$(GOOS).*
//     name_$(GOARCH).*
//     name_$(GOOS)_$(GOARCH).*
//     name_$(GOOS)_test.*
//     name_$(GOARCH)_test.*
//     name_$(GOOS)_$(GOARCH)_test.*
//
func isOSArchFile(ctxt *build.Context, name string) bool {
	if dot := strings.Index(name, "."); dot != -1 {
		name = name[:dot]
	}
	l := strings.Split(name, "_")
	if n := len(l); n > 0 && l[n-1] == "test" {
		l = l[:n-1]
	}
	n := len(l)
	if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] {
		return l[n-2] == ctxt.GOOS && l[n-1] == ctxt.GOARCH
	}
	if n >= 1 && knownOS[l[n-1]] {
		return l[n-1] == ctxt.GOOS
	}
	if n >= 1 && knownArch[l[n-1]] {
		return l[n-1] == ctxt.GOARCH
	}
	return false
}

var knownOS = make(map[string]bool)
var knownArch = make(map[string]bool)

func init() {
	for _, v := range strings.Fields(goosList) {
		knownOS[v] = true
	}
	for _, v := range strings.Fields(goarchList) {
		knownArch[v] = true
	}
}
